diff options
Diffstat (limited to 'apps/encryption/lib')
-rw-r--r-- | apps/encryption/lib/crypto/crypt.php | 457 | ||||
-rw-r--r-- | apps/encryption/lib/crypto/encryption.php | 328 | ||||
-rw-r--r-- | apps/encryption/lib/exceptions/multikeydecryptexception.php | 9 | ||||
-rw-r--r-- | apps/encryption/lib/exceptions/multikeyencryptexception.php | 9 | ||||
-rw-r--r-- | apps/encryption/lib/exceptions/privatekeymissingexception.php | 38 | ||||
-rw-r--r-- | apps/encryption/lib/exceptions/publickeymissingexception.php | 20 | ||||
-rw-r--r-- | apps/encryption/lib/hookmanager.php | 66 | ||||
-rw-r--r-- | apps/encryption/lib/keymanager.php | 511 | ||||
-rw-r--r-- | apps/encryption/lib/recovery.php | 316 | ||||
-rw-r--r-- | apps/encryption/lib/session.php | 114 | ||||
-rw-r--r-- | apps/encryption/lib/users/setup.php | 72 | ||||
-rw-r--r-- | apps/encryption/lib/util.php | 117 |
12 files changed, 2057 insertions, 0 deletions
diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php new file mode 100644 index 00000000000..c0b737a3daa --- /dev/null +++ b/apps/encryption/lib/crypto/crypt.php @@ -0,0 +1,457 @@ +<?php +/** + * @author Clark Tomlinson <clark@owncloud.com> + * @since 2/19/15, 1:42 PM + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Encryption\Crypto; + + +use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Encryption\Exceptions\EncryptionFailedException; +use OCA\Encryption\Exceptions\MultiKeyDecryptException; +use OCA\Encryption\Exceptions\MultiKeyEncryptException; +use OCP\Encryption\Exceptions\GenericEncryptionException; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IUser; +use OCP\IUserSession; + +class Crypt { + + const DEFAULT_CIPHER = 'AES-256-CFB'; + + const HEADER_START = 'HBEGIN'; + const HEADER_END = 'HEND'; + /** + * @var ILogger + */ + private $logger; + /** + * @var IUser + */ + private $user; + /** + * @var IConfig + */ + private $config; + + /** + * @param ILogger $logger + * @param IUserSession $userSession + * @param IConfig $config + */ + public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config) { + $this->logger = $logger; + $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false; + $this->config = $config; + } + + /** + * @return array|bool + */ + public function createKeyPair() { + + $log = $this->logger; + $res = $this->getOpenSSLPKey(); + + if (!$res) { + $log->error("Encryption Library could'nt generate users key-pair for {$this->user->getUID()}", + ['app' => 'encryption']); + + if (openssl_error_string()) { + $log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(), + ['app' => 'encryption']); + } + } elseif (openssl_pkey_export($res, + $privateKey, + null, + $this->getOpenSSLConfig())) { + $keyDetails = openssl_pkey_get_details($res); + $publicKey = $keyDetails['key']; + + return [ + 'publicKey' => $publicKey, + 'privateKey' => $privateKey + ]; + } + $log->error('Encryption library couldn\'t export users private key, please check your servers openSSL configuration.' . $this->user->getUID(), + ['app' => 'encryption']); + if (openssl_error_string()) { + $log->error('Encryption Library:' . openssl_error_string(), + ['app' => 'encryption']); + } + + return false; + } + + /** + * @return resource + */ + public function getOpenSSLPKey() { + $config = $this->getOpenSSLConfig(); + return openssl_pkey_new($config); + } + + /** + * @return array + */ + private function getOpenSSLConfig() { + $config = ['private_key_bits' => 4096]; + $config = array_merge(\OC::$server->getConfig()->getSystemValue('openssl', + []), + $config); + return $config; + } + + /** + * @param string $plainContent + * @param string $passPhrase + * @return bool|string + * @throws GenericEncryptionException + */ + public function symmetricEncryptFileContent($plainContent, $passPhrase) { + + if (!$plainContent) { + $this->logger->error('Encryption Library, symmetrical encryption failed no content given', + ['app' => 'encryption']); + return false; + } + + $iv = $this->generateIv(); + + $encryptedContent = $this->encrypt($plainContent, + $iv, + $passPhrase, + $this->getCipher()); + // combine content to encrypt the IV identifier and actual IV + $catFile = $this->concatIV($encryptedContent, $iv); + $padded = $this->addPadding($catFile); + + return $padded; + } + + /** + * @param string $plainContent + * @param string $iv + * @param string $passPhrase + * @param string $cipher + * @return string + * @throws EncryptionFailedException + */ + private function encrypt($plainContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) { + $encryptedContent = openssl_encrypt($plainContent, + $cipher, + $passPhrase, + false, + $iv); + + if (!$encryptedContent) { + $error = 'Encryption (symmetric) of content failed'; + $this->logger->error($error . openssl_error_string(), + ['app' => 'encryption']); + throw new EncryptionFailedException($error); + } + + return $encryptedContent; + } + + /** + * @return mixed|string + */ + public function getCipher() { + $cipher = $this->config->getSystemValue('cipher', self::DEFAULT_CIPHER); + if ($cipher !== 'AES-256-CFB' && $cipher !== 'AES-128-CFB') { + $this->logger->warning('Wrong cipher defined in config.php only AES-128-CFB and AES-256-CFB are supported. Fall back' . self::DEFAULT_CIPHER, + ['app' => 'encryption']); + $cipher = self::DEFAULT_CIPHER; + } + + return $cipher; + } + + /** + * @param string $encryptedContent + * @param string $iv + * @return string + */ + private function concatIV($encryptedContent, $iv) { + return $encryptedContent . '00iv00' . $iv; + } + + /** + * @param $data + * @return string + */ + private function addPadding($data) { + return $data . 'xx'; + } + + /** + * @param string $recoveryKey + * @param string $password + * @return bool|string + */ + public function decryptPrivateKey($recoveryKey, $password) { + + $header = $this->parseHeader($recoveryKey); + $cipher = $this->getCipher(); + + // If we found a header we need to remove it from the key we want to decrypt + if (!empty($header)) { + $recoveryKey = substr($recoveryKey, + strpos($recoveryKey, + self::HEADER_END) + strlen(self::HEADER_START)); + } + + $plainKey = $this->symmetricDecryptFileContent($recoveryKey, + $password, + $cipher); + + // Check if this is a valid private key + $res = openssl_get_privatekey($plainKey); + if (is_resource($res)) { + $sslInfo = openssl_pkey_get_details($res); + if (!isset($sslInfo['key'])) { + return false; + } + } else { + return false; + } + + return $plainKey; + } + + /** + * @param $keyFileContents + * @param string $passPhrase + * @param string $cipher + * @return string + * @throws DecryptionFailedException + */ + public function symmetricDecryptFileContent($keyFileContents, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) { + // Remove Padding + $noPadding = $this->removePadding($keyFileContents); + + $catFile = $this->splitIv($noPadding); + + return $this->decrypt($catFile['encrypted'], + $catFile['iv'], + $passPhrase, + $cipher); + } + + /** + * @param $padded + * @return bool|string + */ + private function removePadding($padded) { + if (substr($padded, -2) === 'xx') { + return substr($padded, 0, -2); + } + return false; + } + + /** + * @param $catFile + * @return array + */ + private function splitIv($catFile) { + // Fetch encryption metadata from end of file + $meta = substr($catFile, -22); + + // Fetch IV from end of file + $iv = substr($meta, -16); + + // Remove IV and IV Identifier text to expose encrypted content + + $encrypted = substr($catFile, 0, -22); + + return [ + 'encrypted' => $encrypted, + 'iv' => $iv + ]; + } + + /** + * @param $encryptedContent + * @param $iv + * @param string $passPhrase + * @param string $cipher + * @return string + * @throws DecryptionFailedException + */ + private function decrypt($encryptedContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) { + $plainContent = openssl_decrypt($encryptedContent, + $cipher, + $passPhrase, + false, + $iv); + + if ($plainContent) { + return $plainContent; + } else { + throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string()); + } + } + + /** + * @param $data + * @return array + */ + private function parseHeader($data) { + $result = []; + + if (substr($data, 0, strlen(self::HEADER_START)) === self::HEADER_START) { + $endAt = strpos($data, self::HEADER_END); + $header = substr($data, 0, $endAt + strlen(self::HEADER_END)); + + // +1 not to start with an ':' which would result in empty element at the beginning + $exploded = explode(':', + substr($header, strlen(self::HEADER_START) + 1)); + + $element = array_shift($exploded); + + while ($element != self::HEADER_END) { + $result[$element] = array_shift($exploded); + $element = array_shift($exploded); + } + } + + return $result; + } + + /** + * @return string + * @throws GenericEncryptionException + */ + private function generateIv() { + $random = openssl_random_pseudo_bytes(12, $strong); + if ($random) { + if (!$strong) { + // If OpenSSL indicates randomness is insecure log error + $this->logger->error('Encryption Library: Insecure symmetric key was generated using openssl_random_psudo_bytes()', + ['app' => 'encryption']); + } + + /* + * We encode the iv purely for string manipulation + * purposes -it gets decoded before use + */ + return base64_encode($random); + } + // If we ever get here we've failed anyway no need for an else + throw new GenericEncryptionException('Generating IV Failed'); + } + + /** + * Generate a pseudo random 256-bit ASCII key, used as file key + * @return string + */ + public static function generateFileKey() { + // Generate key + $key = base64_encode(openssl_random_pseudo_bytes(32, $strong)); + if (!$key || !$strong) { + // If OpenSSL indicates randomness is insecure, log error + throw new \Exception('Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()'); + } + + return $key; + } + + /** + * Check if a file's contents contains an IV and is symmetrically encrypted + * + * @param $content + * @return bool + */ + public function isCatFileContent($content) { + if (!$content) { + return false; + } + + $noPadding = $this->removePadding($content); + + // Fetch encryption metadata from end of file + $meta = substr($noPadding, -22); + + // Fetch identifier from start of metadata + $identifier = substr($meta, 0, 6); + + if ($identifier === '00iv00') { + return true; + } + return false; + } + + /** + * @param $encKeyFile + * @param $shareKey + * @param $privateKey + * @return mixed + * @throws MultiKeyDecryptException + */ + public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) { + if (!$encKeyFile) { + throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content'); + } + + if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey)) { + return $plainContent; + } else { + throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string()); + } + } + + /** + * @param $plainContent + * @param array $keyFiles + * @return array + * @throws MultiKeyEncryptException + */ + public function multiKeyEncrypt($plainContent, array $keyFiles) { + // openssl_seal returns false without errors if plaincontent is empty + // so trigger our own error + if (empty($plainContent)) { + throw new MultiKeyEncryptException('Cannot multikeyencrypt empty plain content'); + } + + // Set empty vars to be set by openssl by reference + $sealed = ''; + $shareKeys = []; + $mappedShareKeys = []; + + if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles)) { + $i = 0; + + // Ensure each shareKey is labelled with its corresponding key id + foreach ($keyFiles as $userId => $publicKey) { + $mappedShareKeys[$userId] = $shareKeys[$i]; + $i++; + } + + return [ + 'keys' => $mappedShareKeys, + 'data' => $sealed + ]; + } else { + throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string()); + } + } +} + diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php new file mode 100644 index 00000000000..7c633b7411f --- /dev/null +++ b/apps/encryption/lib/crypto/encryption.php @@ -0,0 +1,328 @@ +<?php +/** + * @author Clark Tomlinson <fallen013@gmail.com> + * @since 3/6/15, 2:28 PM + * @link http:/www.clarkt.com + * @copyright Clark Tomlinson © 2015 + * + */ + +namespace OCA\Encryption\Crypto; + + +use OCA\Encryption\Util; +use OCP\Encryption\IEncryptionModule; +use OCA\Encryption\KeyManager; + +class Encryption implements IEncryptionModule { + + const ID = 'OC_DEFAULT_MODULE'; + + /** + * @var Crypt + */ + private $crypt; + + /** @var string */ + private $cipher; + + /** @var string */ + private $path; + + /** @var string */ + private $user; + + /** @var string */ + private $fileKey; + + /** @var string */ + private $writeCache; + + /** @var KeyManager */ + private $keyManager; + + /** @var array */ + private $accessList; + + /** @var boolean */ + private $isWriteOperation; + + /** @var Util */ + private $util; + + /** + * + * @param \OCA\Encryption\Crypto\Crypt $crypt + * @param KeyManager $keyManager + * @param Util $util + */ + public function __construct(Crypt $crypt, KeyManager $keyManager, Util $util) { + $this->crypt = $crypt; + $this->keyManager = $keyManager; + $this->util = $util; + } + + /** + * @return string defining the technical unique id + */ + public function getId() { + return self::ID; + } + + /** + * In comparison to getKey() this function returns a human readable (maybe translated) name + * + * @return string + */ + public function getDisplayName() { + return 'ownCloud Default Encryption'; + } + + /** + * start receiving chunks from a file. This is the place where you can + * perform some initial step before starting encrypting/decrypting the + * chunks + * + * @param string $path to the file + * @param string $user who read/write the file + * @param array $header contains the header data read from the file + * @param array $accessList who has access to the file contains the key 'users' and 'public' + * + * @return array $header contain data as key-value pairs which should be + * written to the header, in case of a write operation + * or if no additional data is needed return a empty array + */ + public function begin($path, $user, $header, $accessList) { + + if (isset($header['cipher'])) { + $this->cipher = $header['cipher']; + } else { + $this->cipher = $this->crypt->getCipher(); + } + + $this->path = $this->getPathToRealFile($path); + $this->accessList = $accessList; + $this->user = $user; + $this->writeCache = ''; + $this->isWriteOperation = false; + + $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user); + + return array('cipher' => $this->cipher); + } + + /** + * last chunk received. This is the place where you can perform some final + * operation and return some remaining data if something is left in your + * buffer. + * + * @param string $path to the file + * @return string remained data which should be written to the file in case + * of a write operation + */ + public function end($path) { + $result = ''; + if ($this->isWriteOperation) { + if (!empty($this->writeCache)) { + $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey); + $this->writeCache = ''; + } + $publicKeys = array(); + foreach ($this->accessList['users'] as $uid) { + $publicKeys[$uid] = $this->keyManager->getPublicKey($uid); + } + + $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys); + + $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys); + $this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles); + } + return $result; + } + + /** + * encrypt data + * + * @param string $data you want to encrypt + * @return mixed encrypted data + */ + public function encrypt($data) { + $this->isWriteOperation = true; + if (empty($this->fileKey)) { + $this->fileKey = $this->crypt->generateFileKey(); + } + + // If extra data is left over from the last round, make sure it + // is integrated into the next 6126 / 8192 block + if ($this->writeCache) { + + // Concat writeCache to start of $data + $data = $this->writeCache . $data; + + // Clear the write cache, ready for reuse - it has been + // flushed and its old contents processed + $this->writeCache = ''; + + } + + $encrypted = ''; + // While there still remains some data to be processed & written + while (strlen($data) > 0) { + + // Remaining length for this iteration, not of the + // entire file (may be greater than 8192 bytes) + $remainingLength = strlen($data); + + // If data remaining to be written is less than the + // size of 1 6126 byte block + if ($remainingLength < 6126) { + + // Set writeCache to contents of $data + // The writeCache will be carried over to the + // next write round, and added to the start of + // $data to ensure that written blocks are + // always the correct length. If there is still + // data in writeCache after the writing round + // has finished, then the data will be written + // to disk by $this->flush(). + $this->writeCache = $data; + + // Clear $data ready for next round + $data = ''; + + } else { + + // Read the chunk from the start of $data + $chunk = substr($data, 0, 6126); + + $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey); + + // Remove the chunk we just processed from + // $data, leaving only unprocessed data in $data + // var, for handling on the next round + $data = substr($data, 6126); + + } + + } + + return $encrypted; + } + + /** + * decrypt data + * + * @param string $data you want to decrypt + * @return mixed decrypted data + */ + public function decrypt($data) { + $result = ''; + if (!empty($data)) { + $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey); + } + return $result; + } + + /** + * update encrypted file, e.g. give additional users access to the file + * + * @param string $path path to the file which should be updated + * @param string $uid of the user who performs the operation + * @param array $accessList who has access to the file contains the key 'users' and 'public' + * @return boolean + */ + public function update($path, $uid, $accessList) { + $fileKey = $this->keyManager->getFileKey($path, $uid); + $publicKeys = array(); + foreach ($accessList['users'] as $user) { + $publicKeys[$user] = $this->keyManager->getPublicKey($user); + } + + $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys); + + $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + + $this->keyManager->deleteAllFileKeys($path); + + $this->keyManager->setAllFileKeys($path, $encryptedFileKey); + + return true; + } + + /** + * add system keys such as the public share key and the recovery key + * + * @param array $accessList + * @param array $publicKeys + * @return array + */ + public function addSystemKeys(array $accessList, array $publicKeys) { + if (!empty($accessList['public'])) { + $publicKeys[$this->keyManager->getPublicShareKeyId()] = $this->keyManager->getPublicShareKey(); + } + + if ($this->keyManager->recoveryKeyExists() && + $this->util->recoveryEnabled($this->user)) { + + $publicKeys[$this->keyManager->getRecoveryKeyId()] = $this->keyManager->getRecoveryKey(); + } + + + return $publicKeys; + } + + + /** + * should the file be encrypted or not + * + * @param string $path + * @return boolean + */ + public function shouldEncrypt($path) { + $parts = explode('/', $path); + if (count($parts) < 3) { + return false; + } + + if ($parts[2] == 'files') { + return true; + } + if ($parts[2] == 'files_versions') { + return true; + } + + return false; + } + + /** + * calculate unencrypted size + * + * @param string $path to file + * @return integer unencrypted size + */ + public function calculateUnencryptedSize($path) { + // TODO: Implement calculateUnencryptedSize() method. + } + + /** + * get size of the unencrypted payload per block. + * ownCloud read/write files with a block size of 8192 byte + * + * @return integer + */ + public function getUnencryptedBlockSize() { + return 6126; + } + + protected function getPathToRealFile($path) { + $realPath = $path; + $parts = explode('/', $path); + if ($parts[2] === 'files_versions') { + $realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3)); + $length = strrpos($realPath, '.'); + $realPath = substr($realPath, 0, $length); + } + + return $realPath; + } +} diff --git a/apps/encryption/lib/exceptions/multikeydecryptexception.php b/apps/encryption/lib/exceptions/multikeydecryptexception.php new file mode 100644 index 00000000000..1466d35eda3 --- /dev/null +++ b/apps/encryption/lib/exceptions/multikeydecryptexception.php @@ -0,0 +1,9 @@ +<?php + +namespace OCA\Encryption\Exceptions; + +use OCP\Encryption\Exceptions\GenericEncryptionException; + +class MultiKeyDecryptException extends GenericEncryptionException { + +} diff --git a/apps/encryption/lib/exceptions/multikeyencryptexception.php b/apps/encryption/lib/exceptions/multikeyencryptexception.php new file mode 100644 index 00000000000..daf528e2cf7 --- /dev/null +++ b/apps/encryption/lib/exceptions/multikeyencryptexception.php @@ -0,0 +1,9 @@ +<?php + +namespace OCA\Encryption\Exceptions; + +use OCP\Encryption\Exceptions\GenericEncryptionException; + +class MultiKeyEncryptException extends GenericEncryptionException { + +} diff --git a/apps/encryption/lib/exceptions/privatekeymissingexception.php b/apps/encryption/lib/exceptions/privatekeymissingexception.php new file mode 100644 index 00000000000..50d75870b20 --- /dev/null +++ b/apps/encryption/lib/exceptions/privatekeymissingexception.php @@ -0,0 +1,38 @@ +<?php + /** + * @author Clark Tomlinson <clark@owncloud.com> + * @since 2/25/15, 9:39 AM + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Encryption\Exceptions; + +use OCP\Encryption\Exceptions\GenericEncryptionException; + +class PrivateKeyMissingException extends GenericEncryptionException { + + /** + * @param string $userId + */ + public function __construct($userId) { + if(empty($userId)) { + $userId = "<no-user-id-given>"; + } + parent::__construct("Private Key missing for user: $userId"); + } + +} diff --git a/apps/encryption/lib/exceptions/publickeymissingexception.php b/apps/encryption/lib/exceptions/publickeymissingexception.php new file mode 100644 index 00000000000..9638c28e427 --- /dev/null +++ b/apps/encryption/lib/exceptions/publickeymissingexception.php @@ -0,0 +1,20 @@ +<?php + + +namespace OCA\Encryption\Exceptions; + +use OCP\Encryption\Exceptions\GenericEncryptionException; + +class PublicKeyMissingException extends GenericEncryptionException { + + /** + * @param string $userId + */ + public function __construct($userId) { + if(empty($userId)) { + $userId = "<no-user-id-given>"; + } + parent::__construct("Public Key missing for user: $userId"); + } + +} diff --git a/apps/encryption/lib/hookmanager.php b/apps/encryption/lib/hookmanager.php new file mode 100644 index 00000000000..19ee142a622 --- /dev/null +++ b/apps/encryption/lib/hookmanager.php @@ -0,0 +1,66 @@ +<?php +/** + * @author Clark Tomlinson <clark@owncloud.com> + * @since 2/19/15, 10:13 AM + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Encryption; + + +use OCA\Encryption\Hooks\Contracts\IHook; + +class HookManager { + + private $hookInstances = []; + + /** + * @param array|IHook $instances + * - This accepts either a single instance of IHook or an array of instances of IHook + * @return bool + */ + public function registerHook($instances) { + if (is_array($instances)) { + foreach ($instances as $instance) { + if (!$instance instanceof IHook) { + return false; + } + $this->hookInstances[] = $instance; + } + + } elseif ($instances instanceof IHook) { + $this->hookInstances[] = $instances; + } + return true; + } + + /** + * + */ + public function fireHooks() { + foreach ($this->hookInstances as $instance) { + /** + * Fire off the add hooks method of each instance stored in cache + * + * @var $instance IHook + */ + $instance->addHooks(); + } + + } + +} diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php new file mode 100644 index 00000000000..1f71a891e81 --- /dev/null +++ b/apps/encryption/lib/keymanager.php @@ -0,0 +1,511 @@ +<?php + +namespace OCA\Encryption; + +use OC\Encryption\Exceptions\DecryptionFailedException; +use OCA\Encryption\Exceptions\PrivateKeyMissingException; +use OCA\Encryption\Exceptions\PublicKeyMissingException; +use OCA\Encryption\Crypto\Crypt; +use OCP\Encryption\Keys\IStorage; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IUserSession; + +class KeyManager { + + /** + * @var Session + */ + protected $session; + /** + * @var IStorage + */ + private $keyStorage; + /** + * @var Crypt + */ + private $crypt; + /** + * @var string + */ + private $recoveryKeyId; + /** + * @var string + */ + private $publicShareKeyId; + /** + * @var string UserID + */ + private $keyId; + /** + * @var string + */ + private $publicKeyId = 'publicKey'; + /** + * @var string + */ + private $privateKeyId = 'privateKey'; + + /** + * @var string + */ + private $shareKeyId = 'shareKey'; + + /** + * @var string + */ + private $fileKeyId = 'fileKey'; + /** + * @var IConfig + */ + private $config; + /** + * @var ILogger + */ + private $log; + /** + * @var Util + */ + private $util; + + /** + * @param IStorage $keyStorage + * @param Crypt $crypt + * @param IConfig $config + * @param IUserSession $userSession + * @param Session $session + * @param ILogger $log + * @param Util $util + */ + public function __construct( + IStorage $keyStorage, + Crypt $crypt, + IConfig $config, + IUserSession $userSession, + Session $session, + ILogger $log, + Util $util + ) { + + $this->util = $util; + $this->session = $session; + $this->keyStorage = $keyStorage; + $this->crypt = $crypt; + $this->config = $config; + $this->log = $log; + + $this->recoveryKeyId = $this->config->getAppValue('encryption', + 'recoveryKeyId'); + if (empty($this->recoveryKeyId)) { + $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8); + $this->config->setAppValue('encryption', + 'recoveryKeyId', + $this->recoveryKeyId); + } + + $this->publicShareKeyId = $this->config->getAppValue('encryption', + 'publicShareKeyId'); + if (empty($this->publicShareKeyId)) { + $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); + $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); + } + + $shareKey = $this->getPublicShareKey(); + if (empty($shareKey)) { + $keyPair = $this->crypt->createKeyPair(); + + // Save public key + $this->keyStorage->setSystemUserKey( + $this->publicShareKeyId . '.publicKey', $keyPair['publicKey']); + + // Encrypt private key empty passphrase + $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], ''); + $this->keyStorage->setSystemUserKey($this->publicShareKeyId . '.privateKey', $encryptedKey); + } + + $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; + $this->log = $log; + } + + /** + * @return bool + */ + public function recoveryKeyExists() { + $key = $this->getRecoveryKey(); + return (!empty($key)); + } + + /** + * get recovery key + * + * @return string + */ + public function getRecoveryKey() { + return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey'); + } + + /** + * get recovery key ID + * + * @return string + */ + public function getRecoveryKeyId() { + return $this->recoveryKeyId; + } + + /** + * @param $password + * @return bool + */ + public function checkRecoveryPassword($password) { + $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey'); + $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, + $password); + + if ($decryptedRecoveryKey) { + return true; + } + return false; + } + + /** + * @param string $uid + * @param string $password + * @param string $keyPair + * @return bool + */ + public function storeKeyPair($uid, $password, $keyPair) { + // Save Public Key + $this->setPublicKey($uid, $keyPair['publicKey']); + + $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], + $password); + + if ($encryptedKey) { + $this->setPrivateKey($uid, $encryptedKey); + return true; + } + return false; + } + + /** + * @param string $password + * @param array $keyPair + * @return bool + */ + public function setRecoveryKey($password, $keyPair) { + // Save Public Key + $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId(). '.publicKey', $keyPair['publicKey']); + + $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], + $password); + + if ($encryptedKey) { + $this->setSystemPrivateKey($this->getRecoveryKeyId(), $encryptedKey); + return true; + } + return false; + } + + /** + * @param $userId + * @param $key + * @return bool + */ + public function setPublicKey($userId, $key) { + return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key); + } + + /** + * @param $userId + * @param $key + * @return bool + */ + public function setPrivateKey($userId, $key) { + return $this->keyStorage->setUserKey($userId, + $this->privateKeyId, + $key); + } + + /** + * write file key to key storage + * + * @param string $path + * @param string $key + * @return boolean + */ + public function setFileKey($path, $key) { + return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key); + } + + /** + * set all file keys (the file key and the corresponding share keys) + * + * @param string $path + * @param array $keys + */ + public function setAllFileKeys($path, $keys) { + $this->setFileKey($path, $keys['data']); + foreach ($keys['keys'] as $uid => $keyFile) { + $this->setShareKey($path, $uid, $keyFile); + } + } + + /** + * write share key to the key storage + * + * @param string $path + * @param string $uid + * @param string $key + * @return boolean + */ + public function setShareKey($path, $uid, $key) { + $keyId = $uid . '.' . $this->shareKeyId; + return $this->keyStorage->setFileKey($path, $keyId, $key); + } + + /** + * Decrypt private key and store it + * + * @param string $uid userid + * @param string $passPhrase users password + * @return boolean + */ + public function init($uid, $passPhrase) { + try { + $privateKey = $this->getPrivateKey($uid); + $privateKey = $this->crypt->decryptPrivateKey($privateKey, + $passPhrase); + } catch (PrivateKeyMissingException $e) { + return false; + } catch (DecryptionFailedException $e) { + return false; + } + + $this->session->setPrivateKey($privateKey); + $this->session->setStatus(Session::INIT_SUCCESSFUL); + + return true; + } + + /** + * @param $userId + * @return mixed + * @throws PrivateKeyMissingException + */ + public function getPrivateKey($userId) { + $privateKey = $this->keyStorage->getUserKey($userId, + $this->privateKeyId); + + if (strlen($privateKey) !== 0) { + return $privateKey; + } + throw new PrivateKeyMissingException($userId); + } + + /** + * @param $path + * @param $uid + * @return string + */ + public function getFileKey($path, $uid) { + $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId); + + if (is_null($uid)) { + $uid = $this->getPublicShareKeyId(); + $shareKey = $this->getShareKey($path, $uid); + $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey'); + $privateKey = $this->crypt->symmetricDecryptFileContent($privateKey); + } else { + $shareKey = $this->getShareKey($path, $uid); + $privateKey = $this->session->getPrivateKey(); + } + + if ($encryptedFileKey && $shareKey && $privateKey) { + return $this->crypt->multiKeyDecrypt($encryptedFileKey, + $shareKey, + $privateKey); + } + + return ''; + } + + /** + * get the encrypted file key + * + * @param $path + * @return string + */ + public function getEncryptedFileKey($path) { + $encryptedFileKey = $this->keyStorage->getFileKey($path, + $this->fileKeyId); + + return $encryptedFileKey; + } + + /** + * delete share key + * + * @param string $path + * @param string $keyId + * @return boolean + */ + public function deleteShareKey($path, $keyId) { + return $this->keyStorage->deleteFileKey($path, $keyId . '.' . $this->shareKeyId); + } + + + /** + * @param $path + * @param $uid + * @return mixed + */ + public function getShareKey($path, $uid) { + $keyId = $uid . '.' . $this->shareKeyId; + return $this->keyStorage->getFileKey($path, $keyId); + } + + /** + * @param $userId + * @return bool + */ + public function userHasKeys($userId) { + try { + $this->getPrivateKey($userId); + $this->getPublicKey($userId); + } catch (PrivateKeyMissingException $e) { + return false; + } catch (PublicKeyMissingException $e) { + return false; + } + return true; + } + + /** + * @param $userId + * @return mixed + * @throws PublicKeyMissingException + */ + public function getPublicKey($userId) { + $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId); + + if (strlen($publicKey) !== 0) { + return $publicKey; + } + throw new PublicKeyMissingException($userId); + } + + public function getPublicShareKeyId() { + return $this->publicShareKeyId; + } + + /** + * get public key for public link shares + * + * @return string + */ + public function getPublicShareKey() { + return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey'); + } + + /** + * @param $purpose + * @param bool $timestamp + * @param bool $includeUserKeys + */ + public function backupAllKeys($purpose, $timestamp = true, $includeUserKeys = true) { +// $backupDir = $this->keyStorage->; + } + + /** + * @param string $uid + */ + public function replaceUserKeys($uid) { + $this->backupAllKeys('password_reset'); + $this->deletePublicKey($uid); + $this->deletePrivateKey($uid); + } + + /** + * @param $uid + * @return bool + */ + public function deletePublicKey($uid) { + return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId); + } + + /** + * @param $uid + * @return bool + */ + private function deletePrivateKey($uid) { + return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId); + } + + public function deleteAllFileKeys($path) { + return $this->keyStorage->deleteAllFileKeys($path); + } + + /** + * @param array $userIds + * @return array + * @throws PublicKeyMissingException + */ + public function getPublicKeys(array $userIds) { + $keys = []; + + foreach ($userIds as $userId) { + try { + $keys[$userId] = $this->getPublicKey($userId); + } catch (PublicKeyMissingException $e) { + continue; + } + } + + return $keys; + + } + + /** + * @param string $keyId + * @return string returns openssl key + */ + public function getSystemPrivateKey($keyId) { + return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId); + } + + /** + * @param string $keyId + * @param string $key + * @return string returns openssl key + */ + public function setSystemPrivateKey($keyId, $key) { + return $this->keyStorage->setSystemUserKey($keyId . '.' . $this->privateKeyId, $key); + } + + /** + * add system keys such as the public share key and the recovery key + * + * @param array $accessList + * @param array $publicKeys + * @return array + * @throws PublicKeyMissingException + */ + public function addSystemKeys(array $accessList, array $publicKeys) { + if (!empty($accessList['public'])) { + $publicShareKey = $this->getPublicShareKey(); + if (empty($publicShareKey)) { + throw new PublicKeyMissingException($this->getPublicShareKeyId()); + } + $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey; + } + + if ($this->recoveryKeyExists() && + $this->util->isRecoveryEnabledForUser()) { + + $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey(); + } + + return $publicKeys; + } +} diff --git a/apps/encryption/lib/recovery.php b/apps/encryption/lib/recovery.php new file mode 100644 index 00000000000..34acdd0a6e3 --- /dev/null +++ b/apps/encryption/lib/recovery.php @@ -0,0 +1,316 @@ +<?php +/** + * @author Clark Tomlinson <clark@owncloud.com> + * @since 2/19/15, 11:45 AM + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Encryption; + + +use OCA\Encryption\Crypto\Crypt; +use OCP\Encryption\Keys\IStorage; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserSession; +use OCP\PreConditionNotMetException; +use OCP\Security\ISecureRandom; +use OC\Files\View; +use OCP\Encryption\IFile; + +class Recovery { + + + /** + * @var null|IUser + */ + protected $user; + /** + * @var Crypt + */ + protected $crypt; + /** + * @var ISecureRandom + */ + private $random; + /** + * @var KeyManager + */ + private $keyManager; + /** + * @var IConfig + */ + private $config; + /** + * @var IStorage + */ + private $keyStorage; + /** + * @var View + */ + private $view; + /** + * @var IFile + */ + private $file; + /** + * @var string + */ + private $recoveryKeyId; + + /** + * @param IUserSession $user + * @param Crypt $crypt + * @param ISecureRandom $random + * @param KeyManager $keyManager + * @param IConfig $config + * @param IStorage $keyStorage + * @param IFile $file + * @param View $view + */ + public function __construct(IUserSession $user, + Crypt $crypt, + ISecureRandom $random, + KeyManager $keyManager, + IConfig $config, + IStorage $keyStorage, + IFile $file, + View $view) { + $this->user = ($user && $user->isLoggedIn()) ? $user->getUser() : false; + $this->crypt = $crypt; + $this->random = $random; + $this->keyManager = $keyManager; + $this->config = $config; + $this->keyStorage = $keyStorage; + $this->view = $view; + $this->file = $file; + } + + /** + * @param $recoveryKeyId + * @param $password + * @return bool + */ + public function enableAdminRecovery($password) { + $appConfig = $this->config; + $keyManager = $this->keyManager; + + if (!$keyManager->recoveryKeyExists()) { + $keyPair = $this->crypt->createKeyPair(); + + $this->keyManager->setRecoveryKey($password, $keyPair); + } + + if ($keyManager->checkRecoveryPassword($password)) { + $appConfig->setAppValue('encryption', 'recoveryAdminEnabled', 1); + return true; + } + + return false; + } + + /** + * change recovery key id + * + * @param string $newPassword + * @param string $oldPassword + */ + public function changeRecoveryKeyPassword($newPassword, $oldPassword) { + $recoveryKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId()); + $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $oldPassword); + $encryptedRecoveryKey = $this->crypt->symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword); + if ($encryptedRecoveryKey) { + $this->keyManager->setSystemPrivateKey($this->keyManager->getRecoveryKeyId(), $encryptedRecoveryKey); + return true; + } + return false; + } + + /** + * @param $recoveryPassword + * @return bool + */ + public function disableAdminRecovery($recoveryPassword) { + $keyManager = $this->keyManager; + + if ($keyManager->checkRecoveryPassword($recoveryPassword)) { + // Set recoveryAdmin as disabled + $this->config->setAppValue('encryption', 'recoveryAdminEnabled', 0); + return true; + } + return false; + } + + /** + * check if recovery is enabled for user + * + * @param string $user if no user is given we check the current logged-in user + * + * @return bool + */ + public function isRecoveryEnabledForUser($user = '') { + $uid = empty($user) ? $this->user->getUID() : $user; + $recoveryMode = $this->config->getUserValue($uid, + 'encryption', + 'recoveryEnabled', + 0); + + return ($recoveryMode === '1'); + } + + /** + * check if recovery is key is enabled by the administrator + * + * @return bool + */ + public function isRecoveryKeyEnabled() { + $enabled = $this->config->getAppValue('encryption', 'recoveryAdminEnabled', 0); + + return ($enabled === '1'); + } + + /** + * @param string $value + * @return bool + */ + public function setRecoveryForUser($value) { + + try { + $this->config->setUserValue($this->user->getUID(), + 'encryption', + 'recoveryEnabled', + $value); + + if ($value === '1') { + $this->addRecoveryKeys('/' . $this->user->getUID() . '/files/'); + } else { + $this->removeRecoveryKeys('/' . $this->user->getUID() . '/files/'); + } + + return true; + } catch (PreConditionNotMetException $e) { + return false; + } + } + + /** + * add recovery key to all encrypted files + */ + private function addRecoveryKeys($path) { + $dirContent = $this->view->getDirectoryContent($path); + foreach ($dirContent as $item) { + $filePath = $item->getPath(); + if ($item['type'] === 'dir') { + $this->addRecoveryKeys($filePath . '/'); + } else { + $fileKey = $this->keyManager->getFileKey($filePath, $this->user->getUID()); + if (!empty($fileKey)) { + $accessList = $this->file->getAccessList($filePath); + $publicKeys = array(); + foreach ($accessList['users'] as $uid) { + $publicKeys[$uid] = $this->keyManager->getPublicKey($uid); + } + + $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys); + + $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $this->keyManager->setAllFileKeys($filePath, $encryptedKeyfiles); + } + } + } + } + + /** + * remove recovery key to all encrypted files + */ + private function removeRecoveryKeys($path) { + $dirContent = $this->view->getDirectoryContent($path); + foreach ($dirContent as $item) { + $filePath = $item->getPath(); + if ($item['type'] === 'dir') { + $this->removeRecoveryKeys($filePath . '/'); + } else { + $this->keyManager->deleteShareKey($filePath, $this->keyManager->getRecoveryKeyId()); + } + } + } + + /** + * recover users files with the recovery key + * + * @param string $recoveryPassword + * @param string $user + */ + public function recoverUsersFiles($recoveryPassword, $user) { + $encryptedKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId()); + + $privateKey = $this->crypt->decryptPrivateKey($encryptedKey, + $recoveryPassword); + + $this->recoverAllFiles('/' . $user . '/files/', $privateKey); + } + + /** + * @param $path + * @param $privateKey + */ + private function recoverAllFiles($path, $privateKey) { + $dirContent = $this->view->getDirectoryContent($path); + + foreach ($dirContent as $item) { + // Get relative path from encryption/keyfiles + $filePath = $item->getPath(); + if ($this->view->is_dir($filePath)) { + $this->recoverAllFiles($filePath . '/', $privateKey); + } else { + $this->recoverFile($filePath, $privateKey); + } + } + + } + + /** + * @param string $path + * @param string $privateKey + */ + private function recoverFile($path, $privateKey) { + $encryptedFileKey = $this->keyManager->getEncryptedFileKey($path); + $shareKey = $this->keyManager->getShareKey($path, $this->keyManager->getRecoveryKeyId()); + + if ($encryptedFileKey && $shareKey && $privateKey) { + $fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey, + $shareKey, + $privateKey); + } + + if (!empty($fileKey)) { + $accessList = $this->file->getAccessList($path); + $publicKeys = array(); + foreach ($accessList['users'] as $uid) { + $publicKeys[$uid] = $this->keyManager->getPublicKey($uid); + } + + $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys); + + $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $this->keyManager->setAllFileKeys($path, $encryptedKeyfiles); + } + + } + + +} diff --git a/apps/encryption/lib/session.php b/apps/encryption/lib/session.php new file mode 100644 index 00000000000..e705611fa6e --- /dev/null +++ b/apps/encryption/lib/session.php @@ -0,0 +1,114 @@ +<?php + +/** + * ownCloud + * + * @copyright (C) 2015 ownCloud, Inc. + * + * @author Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCA\Encryption; + +use \OCP\ISession; + +class Session { + + /** @var ISession */ + protected $session; + + const NOT_INITIALIZED = '0'; + const INIT_EXECUTED = '1'; + const INIT_SUCCESSFUL = '2'; + + public function __construct(ISession $session) { + $this->session = $session; + } + + /** + * Sets status of encryption app + * + * @param string $status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INITIALIZED + */ + public function setStatus($status) { + $this->session->set('encryptionInitialized', $status); + } + + /** + * Gets status if we already tried to initialize the encryption app + * + * @return string init status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INITIALIZED + */ + public function getStatus() { + $status = $this->session->get('encryptionInitialized'); + if (is_null($status)) { + $status = self::NOT_INITIALIZED; + } + + return $status; + } + + /** + * Gets user or public share private key from session + * + * @return string $privateKey The user's plaintext private key + * @throws Exceptions\PrivateKeyMissingException + */ + public function getPrivateKey() { + $key = $this->session->get('privateKey'); + if (is_null($key)) { + throw new Exceptions\PrivateKeyMissingException('please try to log-out and log-in again', 0); + } + return $key; + } + + /** + * check if private key is set + * + * @return boolean + */ + public function isPrivateKeySet() { + $key = $this->session->get('privateKey'); + if (is_null($key)) { + return false; + } + + return true; + } + + /** + * Sets user private key to session + * + * @param string $key users private key + * + * @note this should only be set on login + */ + public function setPrivateKey($key) { + $this->session->set('privateKey', $key); + } + + + /** + * remove keys from session + */ + public function clear() { + $this->session->remove('publicSharePrivateKey'); + $this->session->remove('privateKey'); + $this->session->remove('encryptionInitialized'); + + } + +} diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php new file mode 100644 index 00000000000..e80bf6003e6 --- /dev/null +++ b/apps/encryption/lib/users/setup.php @@ -0,0 +1,72 @@ +<?php +/** + * @author Clark Tomlinson <fallen013@gmail.com> + * @since 3/6/15, 11:36 AM + * @link http:/www.clarkt.com + * @copyright Clark Tomlinson © 2015 + * + */ + +namespace OCA\Encryption\Users; + + +use OCA\Encryption\Crypto\Crypt; +use OCA\Encryption\KeyManager; +use OCP\ILogger; +use OCP\IUserSession; + +class Setup { + /** + * @var Crypt + */ + private $crypt; + /** + * @var KeyManager + */ + private $keyManager; + /** + * @var ILogger + */ + private $logger; + /** + * @var bool|string + */ + private $user; + + + /** + * @param ILogger $logger + * @param IUserSession $userSession + * @param Crypt $crypt + * @param KeyManager $keyManager + */ + public function __construct(ILogger $logger, IUserSession $userSession, Crypt $crypt, KeyManager $keyManager) { + $this->logger = $logger; + $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; + $this->crypt = $crypt; + $this->keyManager = $keyManager; + } + + /** + * @param $uid userid + * @param $password user password + * @return bool + */ + public function setupUser($uid, $password) { + return $this->setupServerSide($uid, $password); + } + + /** + * @param $uid userid + * @param $password user password + * @return bool + */ + public function setupServerSide($uid, $password) { + // Check if user already has keys + if (!$this->keyManager->userHasKeys($uid)) { + return $this->keyManager->storeKeyPair($uid, $password, + $this->crypt->createKeyPair()); + } + return true; + } +} diff --git a/apps/encryption/lib/util.php b/apps/encryption/lib/util.php new file mode 100644 index 00000000000..6b6b8b6b38c --- /dev/null +++ b/apps/encryption/lib/util.php @@ -0,0 +1,117 @@ +<?php +/** + * @author Clark Tomlinson <clark@owncloud.com> + * @since 3/17/15, 10:31 AM + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\Encryption; + + +use OC\Files\View; +use OCA\Encryption\Crypto\Crypt; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IUser; +use OCP\IUserSession; +use OCP\PreConditionNotMetException; + +class Util { + /** + * @var View + */ + private $files; + /** + * @var Crypt + */ + private $crypt; + /** + * @var ILogger + */ + private $logger; + /** + * @var bool|IUser + */ + private $user; + /** + * @var IConfig + */ + private $config; + + /** + * Util constructor. + * + * @param View $files + * @param Crypt $crypt + * @param ILogger $logger + * @param IUserSession $userSession + * @param IConfig $config + */ + public function __construct(View $files, + Crypt $crypt, + ILogger $logger, + IUserSession $userSession, + IConfig $config + ) { + $this->files = $files; + $this->crypt = $crypt; + $this->logger = $logger; + $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false; + $this->config = $config; + } + + /** + * @return bool + */ + public function isRecoveryEnabledForUser() { + $recoveryMode = $this->config->getUserValue($this->user->getUID(), + 'encryption', + 'recoveryEnabled', + 0); + + return ($recoveryMode === '1'); + } + + /** + * @param $enabled + * @return bool + */ + public function setRecoveryForUser($enabled) { + $value = $enabled ? '1' : '0'; + + try { + $this->config->setUserValue($this->user->getUID(), + 'encryption', + 'recoveryEnabled', + $value); + return true; + } catch (PreConditionNotMetException $e) { + return false; + } + } + + /** + * @param string $uid + * @return bool + */ + public function userHasFiles($uid) { + return $this->files->file_exists($uid . '/files'); + } + + +} |