From fd4e59b748d2d22e4aea4a7583139b5a4e4b65d7 Mon Sep 17 00:00:00 2001 From: Sam Tuke Date: Wed, 20 Mar 2013 19:26:59 +0100 Subject: [PATCH] Added method for setting user keyfile recovery preference Fixed method for checking if keyfile recovery is enabled for a user Added unit test for above 2 methods Made proxy{} always use sharing Made proxy{} work regardless of sharing API enabled or not Implemented proxy-based sharing to admin if user keyfile recovery is enabled --- apps/files_encryption/hooks/hooks.php | 2 - apps/files_encryption/lib/keymanager.php | 1 + apps/files_encryption/lib/proxy.php | 58 ++++++++++++------ apps/files_encryption/lib/util.php | 78 +++++++++++++++++++++--- apps/files_encryption/test/util.php | 20 ++++++ 5 files changed, 127 insertions(+), 32 deletions(-) diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 8db7539706..82e650c417 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -40,7 +40,6 @@ class Hooks { \OC\Files\Filesystem::init( $params['uid'] . '/' . 'files' . '/' ); $view = new \OC_FilesystemView( '/' ); - $util = new Util( $view, $params['uid'] ); // Check files_encryption infrastructure is ready for action @@ -61,7 +60,6 @@ class Hooks { $privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] ); $session = new Session(); - $session->setPrivateKey( $privateKey, $params['uid'] ); $view1 = new \OC_FilesystemView( '/' . $params['uid'] ); diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 0c2db2be32..6837dcf67b 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -57,6 +57,7 @@ class Keymanager { return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); \OC_FileProxy::$enabled = true; + } /** diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 2a738c80e3..f469422e22 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -131,32 +131,50 @@ class Proxy extends \OC_FileProxy { // Encrypt data $encData = Crypt::symmetricEncryptFileContent( $data, $plainKey ); - // Check if the keyfile needs to be shared - if ( $userIds = \OCP\Share::getUsersSharingFile( $filePath, true ) ) { - - $publicKeys = Keymanager::getPublicKeys( $rootView, $userIds ); - - \OC_FileProxy::$enabled = false; - - // Encrypt plain keyfile to multiple sharefiles - $multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); - - // Save sharekeys to user folders - // TODO: openssl_seal generates new shareKeys (envelope keys) each time data is encrypted, but will data still be decryptable using old shareKeys? If so we don't need to replace the old shareKeys here, we only need to set the new ones - Keymanager::setShareKeys( $rootView, $filePath, $multiEncrypted['keys'] ); - - // Set encrypted keyfile as common varname - $encKey = $multiEncrypted['encrypted']; + // Check if key recovery is enabled + $recoveryEnabled = $util->recoveryEnabled(); - } else { + // Make sure that a share key is generated for the owner too + $userIds = array( $userId ); - $publicKey = Keymanager::getPublicKey( $rootView, $userId ); + if ( \OCP\Share::isEnabled() ) { - // Encrypt plain data to a single user - $encKey = Crypt::keyEncrypt( $plainKey, $publicKey ); + // Find out who, if anyone, is sharing the file + $shareUids = \OCP\Share::getUsersSharingFile( $filePath, true ); + + $userIds = array_merge( $userIds, $shareUids ); } + // If recovery is enabled, add the + // Admin UID to list of users to share to + if ( $recoveryEnabled ) { + + // FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB? + $adminUid = 'recoveryAdmin'; + + $userIds[] = $adminUid; + + } + + // Remove duplicate UIDs + $uniqueUserIds = array_unique ( $userIds ); + + // Fetch public keys for all users who will share the file + $publicKeys = Keymanager::getPublicKeys( $rootView, $uniqueUserIds ); + + \OC_FileProxy::$enabled = false; + + // Encrypt plain keyfile to multiple sharefiles + $multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); + + // Save sharekeys to user folders + // TODO: openssl_seal generates new shareKeys (envelope keys) each time data is encrypted, but will data still be decryptable using old shareKeys? If so we don't need to replace the old shareKeys here, we only need to set the new ones + Keymanager::setShareKeys( $rootView, $filePath, $multiEncrypted['keys'] ); + + // Set encrypted keyfile as common varname + $encKey = $multiEncrypted['data']; + // Save the key if its new if ( ! $keyPreExists ) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index a80da73a4b..b86e7f421b 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -31,7 +31,12 @@ # ---------------- # Re-use existing keyfiles so they don't need version control (part implemented, stream{} and util{} remain) # Make sure user knows if large files weren't encrypted -# Trashbin support + + +# Test +# ---- +# Test that writing files works when recovery is enabled, and sharing API is disabled +# Test trashbin support // Old Todo: @@ -202,20 +207,73 @@ class Util { } - public function recoveryEnabled( ) { + /** + * @brief Check whether pwd recovery is enabled for a given user + * @return bool + * @note If records are not being returned, check for a hidden space + * at the start of the uid in db + */ + public function recoveryEnabled() { - $sql = 'SELECT * FROM `*PREFIX*myusers` WHERE id = ?'; - $args = array(1); + $sql = 'SELECT + recovery + FROM + `*PREFIX*encryption` + WHERE + uid = ?'; + + $args = array( $this->userId ); - $query = \OCP\DB::prepare($sql); - $result = $query->execute($args); - - while($row = $result->fetchRow()) { - $userName = $row['username']; - } + $query = \OCP\DB::prepare( $sql ); + + $result = $query->execute( $args ); + + // Set default in case no records found + $recoveryEnabled = 0; + + while( $row = $result->fetchRow() ) { + + $recoveryEnabled = $row['recovery']; + + } + + return $recoveryEnabled; } + /** + * @brief Enable / disable pwd recovery for a given user + * @param bool $enabled Whether to enable or disable recovery + * @return bool + */ + public function setRecovery( $enabled ) { + + $sql = 'UPDATE + *PREFIX*encryption + SET + recovery = ? + WHERE + uid = ?'; + + // Ensure value is an integer + $enabled = intval( $enabled ); + + $args = array( $enabled, $this->userId ); + + $query = \OCP\DB::prepare( $sql ); + + if ( $query->execute( $args ) ) { + + return true; + + } else { + + return false; + + } + + } + /** * @brief Find all files and their encryption status within a directory * @param string $directory The path of the parent directory to search diff --git a/apps/files_encryption/test/util.php b/apps/files_encryption/test/util.php index 275e60f4bd..e2767a2ec3 100755 --- a/apps/files_encryption/test/util.php +++ b/apps/files_encryption/test/util.php @@ -164,6 +164,26 @@ class Test_Enc_Util extends \PHPUnit_Framework_TestCase { # then false will be returned. Use strict ordering? } + + function testRecoveryEnabled() { + + $util = new Encryption\Util( $this->view, $this->userId ); + + // Record the value so we can return it to it's original state later + $enabled = $util->recoveryEnabled(); + + $this->assertTrue( $util->setRecovery( 1 ) ); + + $this->assertEquals( 1, $util->recoveryEnabled() ); + + $this->assertTrue( $util->setRecovery( 0 ) ); + + $this->assertEquals( 0, $util->recoveryEnabled() ); + + // Return the setting to it's previous state + $this->assertTrue( $util->setRecovery( $enabled ) ); + + } // /** // * @brief test decryption using legacy blowfish method