From 6ce315fe5826d058722353f3d4c0b2f025dabd43 Mon Sep 17 00:00:00 2001 From: Sam Tuke Date: Tue, 14 Aug 2012 19:06:56 +0100 Subject: [PATCH] added wrapper method in crypt class for encrypting asymmetric and symmetric simultaneously fixed bugs with keymanager integration added unit tests --- apps/files_encryption/hooks/hooks.php | 145 +++++++++--------- apps/files_encryption/lib/crypt.php | 51 +++++- apps/files_encryption/lib/keymanager.php | 27 +++- apps/files_encryption/lib/proxy.php | 25 ++- .../tests/{encryption.php => crypt.php} | 48 +++++- 5 files changed, 199 insertions(+), 97 deletions(-) rename apps/files_encryption/tests/{encryption.php => crypt.php} (80%) diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 5215ac10624..e23e3a09d46 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -1,76 +1,77 @@ -. - * - */ - -namespace OCA_Encryption; - -/** - * Class for hook specific logic - */ - -class Hooks { - - # TODO: use passphrase for encrypting private key that is separate to the login password - - /** - * @brief 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 ( Crypt::mode( $params['uid'] ) == 'server' ) { - - $view = new \OC_FilesystemView( '/' ); - - $util = new Util( $view, $params['uid'] ); - - if ( !$util->ready()) { - - return $util->setupServerSide( $params['password'] ); - - } - - $encryptedKey = Keymanager::getPrivateKey( $params['uid'] ); - - $_SESSION['enckey'] = Crypt::symmetricEncryptFileContent( $encryptedKey, $params['password'] ); - } - - return true; - - } - +. + * + */ + +namespace OCA_Encryption; + +/** + * Class for hook specific logic + */ + +class Hooks { + + # TODO: use passphrase for encrypting private key that is separate to the login password + + /** + * @brief 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 ( Crypt::mode( $params['uid'] ) == 'server' ) { + + $view = new \OC_FilesystemView( '/' ); + + $util = new Util( $view, $params['uid'] ); + + if ( !$util->ready()) { + + return $util->setupServerSide( $params['password'] ); + + } + + $encryptedKey = Keymanager::getPrivateKey( $params['uid'] ); + + $_SESSION['enckey'] = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] ); + + } + + return true; + + } + /** * @brief update the encryption key of the file uploaded by the client - */ - public static function updateKeyfile( $params ) { - if (Crypt::mode() == 'client') - if (isset($params['properties']['key'])) { - Keymanager::setFileKey($params['path'], $params['properties']['key']); - } else { - \OC_Log::write( 'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!", \OC_Log::ERROR ); - error_log("Client side encryption is enabled but the client doesn't provide a encryption key for the file!"); - } - } -} - + */ + public static function updateKeyfile( $params ) { + if (Crypt::mode() == 'client') + if (isset($params['properties']['key'])) { + Keymanager::setFileKey($params['path'], $params['properties']['key']); + } else { + \OC_Log::write( 'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!", \OC_Log::ERROR ); + error_log("Client side encryption is enabled but the client doesn't provide a encryption key for the file!"); + } + } +} + ?> \ No newline at end of file diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 1fa7013776a..f868028be91 100644 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -38,19 +38,19 @@ class Crypt { public static function mode( $user = null ) { $mode = \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ); - + if ( $mode == 'user') { if ( !$user ) { $user = \OCP\User::getUser(); - } - $mode = 'none'; - if ( $user ) { + } + $mode = 'none'; + if ( $user ) { $query = \OC_DB::prepare( "SELECT mode FROM *PREFIX*encryption WHERE uid = ?" ); $result = $query->execute(array($user)); if ($row = $result->fetchRow()){ $mode = $row['mode']; - } - } + } + } } return $mode; @@ -217,6 +217,7 @@ class Crypt { * @param string $source * @param string $target * @param string $key the decryption key + * @returns decrypted content * * This function decrypts a file */ @@ -305,7 +306,7 @@ class Crypt { /** * @brief Asymmetrically encrypt a file using multiple public keys * @param string $plainContent content to be encrypted - * @returns array keys: key, encrypted + * @returns string $plainContent decrypted string * @note symmetricDecryptFileContent() can be used to decrypt files created using this method * * This function decrypts a file @@ -355,6 +356,40 @@ class Crypt { return $plainContent; } + + /** + * @brief Encrypts content symmetrically and generated keyfile asymmetrically + * @returns array keys: data, key + * @note this method is a wrapper for combining other crypt class methods + */ + public static function keyEncryptKeyfile( $plainContent, $publicKey ) { + + // Encrypt plain data, generate keyfile & encrypted file + $cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent ); + + // Encrypt keyfile + $cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey ); + + return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey ); + + } + + /** + * @brief Encrypts content symmetrically and generated keyfile asymmetrically + * @returns decrypted content + * @note this method is a wrapper for combining other crypt class methods + */ + public static function keyDecryptKeyfile( $encryptedData, $encryptedKey, $privateKey ) { + + // Decrypt keyfile + $decryptedKey = self::keyDecrypt( $encryptedKey, $privateKey ); + + // Decrypt encrypted file + $decryptedData = self::symmetricDecryptFileContent( $encryptedData, $decryptedKey ); + + return $decryptedData; + + } /** * @brief Generate a pseudo random 1024kb ASCII key @@ -392,7 +427,7 @@ class Crypt { // $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); // Generate key - if ( $key = base64_encode( openssl_random_pseudo_bytes( 768000, $strong ) ) ) { + if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { if ( !$strong ) { diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 10b673f31aa..8d81c97cfa3 100644 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -41,6 +41,19 @@ class Keymanager { return $view->file_get_contents( '/' . $user.'.private.key' ); } + + /** + * @brief retrieve public key for a specified user + * + * @return string public key or false + */ + public static function getPublicKey() { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView( '/public-keys/' ); + return $view->file_get_contents( '/' . $user . '.public.key' ); + + } /** * @brief retrieve a list of the public key from all users with access to the file @@ -94,22 +107,26 @@ class Keymanager { * @return string file key or false */ public static function getFileKey( $path ) { - + trigger_error("div ".$path); $keypath = ltrim( $path, '/' ); $user = \OCP\User::getUser(); // update $keypath and $user if path point to a file shared by someone else $query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" ); + $result = $query->execute( array ('/'.$user.'/files/'.$keypath, $user)); - if ($row = $result->fetchRow()){ + + if ($row = $result->fetchRow()) { + $keypath = $row['source']; - $keypath_parts=explode('/',$keypath); + $keypath_parts = explode( '/', $keypath ); $user = $keypath_parts[1]; - $keypath = str_replace('/'.$user.'/files/', '', $keypath); + $keypath = str_replace( '/' . $user . '/files/', '', $keypath ); + } $view = new \OC_FilesystemView('/'.$user.'/files_encryption/keyfiles/'); - return $view->file_get_contents($keypath.'.key'); + return $view->file_get_contents( $keypath . '.key' ); } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 85b5c868f30..51ed889d129 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -103,11 +103,14 @@ class Proxy extends \OC_FileProxy { // Set the filesize for userland, before encrypting $size = strlen( $data ); + // Disable encryption proxy to prevent recursive calls + \OC_FileProxy::$enabled = false; + // Encrypt plain data and fetch key - $encrypted = Crypt::symmetricEncryptFileContentKeyfile( $data, $_SESSION['enckey'] ); + $encrypted = Crypt::keyEncryptKeyfile( $data, Keymanager::getPublicKey() ); // Replace plain content with encrypted content by reference - $data = $encrypted['encrypted']; + $data = $encrypted['data']; $filePath = explode( '/', $path ); @@ -119,11 +122,13 @@ class Proxy extends \OC_FileProxy { $view = new \OC_FilesystemView( '/' . \OCP\USER::getUser() . '/files_encryption/keyfiles' ); // Save keyfile for newly encrypted file in parallel directory tree - Keymanager::setFileKey( \OCP\USER::getUser(), $filePath, $encrypted['key'], $view, '\OC_DB', '\OC_FileProxy' ); + Keymanager::setFileKey( $filePath, $encrypted['key'], $view, '\OC_DB' ); // Update the file cache with file info \OC_FileCache::put( $path, array( 'encrypted'=>true, 'size' => $size ), '' ); + \OC_FileProxy::$enabled = true; + } } } @@ -138,14 +143,18 @@ class Proxy extends \OC_FileProxy { $filePath = '/' . implode( '/', $filePath ); - trigger_error( "CAT " . $filePath); - $cached = \OC_FileCache_Cached::get( $path, '' ); - // Get keyfile for encrypted file - $keyFile = Keymanager::getFileKey( \OCP\USER::getUser(), $filePath ); + // Disable encryption proxy to prevent recursive calls + \OC_FileProxy::$enabled = false; - $data = Crypt::symmetricDecryptFileContent( $data, $keyFile ); + $keyFile = Keymanager::getFileKey( $filePath ); + + $privateKey = Keymanager::getPrivateKey(); + + $data = Crypt::keyDecryptKeyfile( $data, $keyFile, $privateKey ); + + \OC_FileProxy::$enabled = true; } diff --git a/apps/files_encryption/tests/encryption.php b/apps/files_encryption/tests/crypt.php similarity index 80% rename from apps/files_encryption/tests/encryption.php rename to apps/files_encryption/tests/crypt.php index ed3b65b1797..b6dc0f40aab 100644 --- a/apps/files_encryption/tests/encryption.php +++ b/apps/files_encryption/tests/crypt.php @@ -7,11 +7,11 @@ * See the COPYING-README file. */ -require realpath( dirname(__FILE__).'/../lib/crypt.php' ); -require realpath( dirname(__FILE__).'/../lib/util.php' ); +require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); +require_once realpath( dirname(__FILE__).'/../lib/util.php' ); //require realpath( dirname(__FILE__).'/../../../lib/filecache.php' ); -class Test_Encryption extends UnitTestCase { +class Test_Crypt extends UnitTestCase { function setUp() { @@ -32,7 +32,7 @@ class Test_Encryption extends UnitTestCase { $this->assertTrue( $key ); - $this->assertTrue( strlen( $key ) > 1000 ); + $this->assertTrue( strlen( $key ) > 16 ); } @@ -137,6 +137,46 @@ class Test_Encryption extends UnitTestCase { $this->assertEqual( $this->data, $decrypt ); } + + function testKeyEncrypt() { + + // Generate keypair + $pair1 = OCA_Encryption\Crypt::createKeypair(); + + // Encrypt data + $crypted = OCA_Encryption\Crypt::keyEncrypt( $this->data, $pair1['publicKey'] ); + + $this->assertNotEqual( $this->data, $crypted ); + + // Decrypt data + $decrypt = OCA_Encryption\Crypt::keyDecrypt( $crypted, $pair1['privateKey'] ); + + $this->assertEqual( $this->data, $decrypt ); + + } + + function testKeyEncryptKeyfile() { + + # TODO: Don't repeat encryption from previous tests, use PHPUnit test interdependency instead + + // Generate keypair + $pair1 = OCA_Encryption\Crypt::createKeypair(); + + // Encrypt plain data, generate keyfile & encrypted file + $cryptedData = OCA_Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->data ); + + // Encrypt keyfile + $cryptedKey = OCA_Encryption\Crypt::keyEncrypt( $cryptedData['key'], $pair1['publicKey'] ); + + // Decrypt keyfile + $decryptKey = OCA_Encryption\Crypt::keyDecrypt( $cryptedKey, $pair1['privateKey'] ); + + // Decrypt encrypted file + $decryptData = OCA_Encryption\Crypt::symmetricDecryptFileContent( $cryptedData['encrypted'], $decryptKey ); + + $this->assertEqual( $this->data, $decryptData ); + + } // function testEncryption(){ //