From 198b73fe322281a297fd8fcc5e7a10762996ce86 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 26 Mar 2015 12:23:36 +0100 Subject: [PATCH] write encrypted file to disc --- apps/encryption/appinfo/encryption.php | 9 +- apps/encryption/lib/crypto/crypt.php | 16 +++ apps/encryption/lib/crypto/encryption.php | 144 +++++++++++++++++++--- apps/encryption/lib/keymanager.php | 8 +- 4 files changed, 152 insertions(+), 25 deletions(-) diff --git a/apps/encryption/appinfo/encryption.php b/apps/encryption/appinfo/encryption.php index a3ca8ec5624..8be3cae5ad5 100644 --- a/apps/encryption/appinfo/encryption.php +++ b/apps/encryption/appinfo/encryption.php @@ -27,7 +27,6 @@ use OCA\Encryption\Crypto\Crypt; use OCA\Encryption\HookManager; use OCA\Encryption\Hooks\UserHooks; use OCA\Encryption\KeyManager; -use OCA\Encryption\Migrator; use OCA\Encryption\Recovery; use OCA\Encryption\Users\Setup; use OCA\Encryption\Util; @@ -98,7 +97,7 @@ class Encryption extends \OCP\AppFramework\App { public function registerEncryptionModule() { $container = $this->getContainer(); $container->registerService('EncryptionModule', function (IAppContainer $c) { - return new \OCA\Encryption\Crypto\Encryption($c->query('Crypt')); + return new \OCA\Encryption\Crypto\Encryption($c->query('Crypt'), $c->query('KeyManager')); }); $module = $container->query('EncryptionModule'); $this->encryptionManager->registerEncryptionModule($module); @@ -122,8 +121,7 @@ class Encryption extends \OCP\AppFramework\App { function (IAppContainer $c) { $server = $c->getServer(); - $moduleId = $c->query('EncryptionModule')->getId(); - return new KeyManager($server->getEncryptionKeyStorage($moduleId), + return new KeyManager($server->getEncryptionKeyStorage(\OCA\Encryption\Crypto\Encryption::ID), $c->query('Crypt'), $server->getConfig(), $server->getUserSession(), @@ -137,14 +135,13 @@ class Encryption extends \OCP\AppFramework\App { function (IAppContainer $c) { $server = $c->getServer(); - $moduleId = $c->query('EncryptionModule')->getId(); return new Recovery( $server->getUserSession(), $c->query('Crypt'), $server->getSecureRandom(), $c->query('KeyManager'), $server->getConfig(), - $server->getEncryptionKeyStorage($moduleId)); + $server->getEncryptionKeyStorage(\OCA\Encryption\Crypto\Encryption::ID)); }); $container->registerService('UserSetup', diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php index 9fb93485ef7..c9f02bfa1cc 100644 --- a/apps/encryption/lib/crypto/crypt.php +++ b/apps/encryption/lib/crypto/crypt.php @@ -25,6 +25,7 @@ namespace OCA\Encryption\Crypto; use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Encryption\Exceptions\EncryptionFailedException; use OC\Encryption\Exceptions\GenericEncryptionException; +use OCA\Encryption\KeyManager; use OCA\Files_Encryption\Exception\MultiKeyDecryptException; use OCA\Files_Encryption\Exception\MultiKeyEncryptException; use OCP\IConfig; @@ -379,6 +380,21 @@ class Crypt { 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 * diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index a29575c11e0..66cb1dc434b 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -11,24 +11,51 @@ namespace OCA\Encryption\Crypto; use OCP\Encryption\IEncryptionModule; +use OCA\Encryption\KeyManager; class Encryption implements IEncryptionModule { + const ID = '42'; + /** * @var Crypt */ private $crypt; - public function __construct(Crypt $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; + + public function __construct(Crypt $crypt, KeyManager $keymanager) { $this->crypt = $crypt; + $this->keymanager = $keymanager; } /** * @return string defining the technical unique id */ public function getId() { - // we need to hard code this value - return md5($this->getDisplayName()); + return self::ID; } /** @@ -46,6 +73,7 @@ class Encryption implements IEncryptionModule { * 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' * @@ -53,9 +81,23 @@ class Encryption implements IEncryptionModule { * 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) { + public function begin($path, $user, $header, $accessList) { + + if (isset($header['cipher'])) { + $this->cipher = $header['cipher']; + } else { + $this->cipher = $this->crypt->getCipher(); + } + + $this->path = $path; + $this->accessList = $accessList; + $this->user = $user; + $this->writeCache = ''; + $this->isWriteOperation = false; - $cipher = $header['']; + $this->fileKey = $this->keymanager->getFileKey($path); + + return array('cipher' => $this->cipher); } /** @@ -68,7 +110,20 @@ class Encryption implements IEncryptionModule { * of a write operation */ public function end($path) { - // TODO: Implement end() method. + $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 $user) { + $publicKeys[] = $this->keymanager->getPublicKey($user); + } + + $result = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys); + } + return $result; } /** @@ -78,21 +133,80 @@ class Encryption implements IEncryptionModule { * @return mixed encrypted data */ public function encrypt($data) { - // Todo: xxx Update Signature and usages - // passphrase is file key decrypted with user private/share key - $this->symmetricEncryptFileContent($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 - * @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); + public function decrypt($data) { + $result = ''; + if (!empty($data)) { + $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey); + } + return $result; } /** @@ -113,7 +227,7 @@ class Encryption implements IEncryptionModule { * @return boolean */ public function shouldEncrypt($path) { - // TODO: Implement shouldEncrypt() method. + return true; } /** @@ -133,6 +247,6 @@ class Encryption implements IEncryptionModule { * @return integer */ public function getUnencryptedBlockSize() { - // TODO: Implement getUnencryptedBlockSize() method. + return 6126; } } diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index c1c1f9811dc..120254defdd 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -62,21 +62,21 @@ class KeyManager { /** * @var string */ - private $publicKeyId = 'public'; + private $publicKeyId = 'publicKey'; /** * @var string */ - private $privateKeyId = 'private'; + private $privateKeyId = 'privateKey'; /** * @var string */ - private $shareKeyId = 'sharekey'; + private $shareKeyId = 'shareKey'; /** * @var string */ - private $fileKeyId = 'filekey'; + private $fileKeyId = 'fileKey'; /** * @var IConfig */ -- 2.39.5