added wrapper method in crypt class for encrypting asymmetric and symmetric simultaneously

fixed bugs with keymanager integration
added unit tests
This commit is contained in:
Sam Tuke 2012-08-14 19:06:56 +01:00
parent b1f6bb36b0
commit 6ce315fe58
5 changed files with 199 additions and 97 deletions

View File

@ -1,76 +1,77 @@
<?php
/**
* ownCloud
*
* @author Sam Tuke
* @copyright 2012 Sam Tuke samtuke@owncloud.org
*
* 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;
/**
* 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;
}
<?php
/**
* ownCloud
*
* @author Sam Tuke
* @copyright 2012 Sam Tuke samtuke@owncloud.org
*
* 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;
/**
* 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!");
}
}
}
?>

View File

@ -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 ) {

View File

@ -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' );
}

View File

@ -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;
}

View File

@ -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(){
//