reformat code

This commit is contained in:
Florin Peter 2013-05-27 17:26:58 +02:00
parent 690bf9b8c4
commit 5d32e214b7
11 changed files with 677 additions and 643 deletions

View File

@ -13,7 +13,7 @@ use OCA\Encryption;
\OCP\JSON::checkAppEnabled('files_encryption'); \OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck(); \OCP\JSON::callCheck();
$l=OC_L10N::get('files_encryption'); $l = OC_L10N::get('files_encryption');
$return = false; $return = false;
@ -21,7 +21,7 @@ $return = false;
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); $recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] == 1){ if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] == 1) {
$return = \OCA\Encryption\Helper::adminEnableRecovery($recoveryKeyId, $_POST['recoveryPassword']); $return = \OCA\Encryption\Helper::adminEnableRecovery($recoveryKeyId, $_POST['recoveryPassword']);
$action = "enable"; $action = "enable";
@ -37,7 +37,12 @@ if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] == 1){
// Return success or failure // Return success or failure
if ($return) { if ($return) {
\OCP\JSON::success(array("data" => array( "message" => $l->t('Recovery key successfully ' . $action.'d')))); \OCP\JSON::success(array("data" => array("message" => $l->t('Recovery key successfully ' . $action . 'd'))));
} else { } else {
\OCP\JSON::error(array("data" => array( "message" => $l->t('Could not '.$action.' recovery key. Please check your recovery key password!')))); \OCP\JSON::error(array(
"data" => array(
"message" => $l->t(
'Could not ' . $action . ' recovery key. Please check your recovery key password!')
)
));
} }

View File

@ -15,7 +15,7 @@ use OCA\Encryption;
\OCP\JSON::checkAppEnabled('files_encryption'); \OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck(); \OCP\JSON::callCheck();
$l=OC_L10N::get('core'); $l = OC_L10N::get('core');
$return = false; $return = false;
@ -46,7 +46,7 @@ if ($result) {
// success or failure // success or failure
if ($return) { if ($return) {
\OCP\JSON::success(array("data" => array( "message" => $l->t('Password successfully changed.')))); \OCP\JSON::success(array("data" => array("message" => $l->t('Password successfully changed.'))));
} else { } else {
\OCP\JSON::error(array("data" => array( "message" => $l->t('Could not change the password. Maybe the old password was not correct.')))); \OCP\JSON::error(array("data" => array("message" => $l->t('Could not change the password. Maybe the old password was not correct.'))));
} }

View File

@ -10,20 +10,20 @@
use OCA\Encryption; use OCA\Encryption;
\OCP\JSON::checkLoggedIn(); \OCP\JSON::checkLoggedIn();
\OCP\JSON::checkAppEnabled( 'files_encryption' ); \OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck(); \OCP\JSON::callCheck();
if ( if (
isset( $_POST['userEnableRecovery'] ) isset($_POST['userEnableRecovery'])
&& ( 0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'] ) && (0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'])
) { ) {
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new \OCA\Encryption\Util( $view, $userId ); $util = new \OCA\Encryption\Util($view, $userId);
// Save recovery preference to DB // Save recovery preference to DB
$return = $util->setRecoveryForUser( $_POST['userEnableRecovery'] ); $return = $util->setRecoveryForUser($_POST['userEnableRecovery']);
if ($_POST['userEnableRecovery'] == "1") { if ($_POST['userEnableRecovery'] == "1") {
$util->addRecoveryKeys(); $util->addRecoveryKeys();
@ -38,4 +38,4 @@ if (
} }
// Return success or failure // Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error(); ($return) ? \OCP\JSON::success() : \OCP\JSON::error();

View File

@ -10,7 +10,7 @@ OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php'; OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php'; OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
OC_FileProxy::register( new OCA\Encryption\Proxy() ); OC_FileProxy::register(new OCA\Encryption\Proxy());
// User related hooks // User related hooks
OCA\Encryption\Helper::registerUserHooks(); OCA\Encryption\Helper::registerUserHooks();
@ -21,7 +21,7 @@ OCA\Encryption\Helper::registerShareHooks();
// Filesystem related hooks // Filesystem related hooks
OCA\Encryption\Helper::registerFilesystemHooks(); OCA\Encryption\Helper::registerFilesystemHooks();
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' ); stream_wrapper_register('crypt', 'OCA\Encryption\Stream');
// check if we are logged in // check if we are logged in
if (OCP\User::isLoggedIn()) { if (OCP\User::isLoggedIn()) {
@ -46,6 +46,6 @@ if (OCP\User::isLoggedIn()) {
} }
// Register settings scripts // Register settings scripts
OCP\App::registerAdmin( 'files_encryption', 'settings-admin' ); OCP\App::registerAdmin('files_encryption', 'settings-admin');
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); OCP\App::registerPersonal('files_encryption', 'settings-personal');

View File

@ -37,58 +37,58 @@ class Hooks {
* @brief Startup encryption backend upon user login * @brief Startup encryption backend upon user login
* @note This method should never be called for users using client side encryption * @note This method should never be called for users using client side encryption
*/ */
public static function login( $params ) { public static function login($params) {
// Manually initialise Filesystem{} singleton with correct // Manually initialise Filesystem{} singleton with correct
// fake root path, in order to avoid fatal webdav errors // fake root path, in order to avoid fatal webdav errors
// NOTE: disabled because this give errors on webdav! // NOTE: disabled because this give errors on webdav!
//\OC\Files\Filesystem::init( $params['uid'], '/' . 'files' . '/' ); //\OC\Files\Filesystem::init( $params['uid'], '/' . 'files' . '/' );
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $params['uid'] ); $util = new Util($view, $params['uid']);
// setup user, if user not ready force relogin // setup user, if user not ready force relogin
if(Helper::setupUser($util, $params['password']) === false) { if (Helper::setupUser($util, $params['password']) === false) {
return false; return false;
} }
$encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] ); $encryptedKey = Keymanager::getPrivateKey($view, $params['uid']);
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] ); $privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, $params['password']);
$session = new Session( $view ); $session = new Session($view);
$session->setPrivateKey( $privateKey, $params['uid'] ); $session->setPrivateKey($privateKey, $params['uid']);
// Check if first-run file migration has already been performed // Check if first-run file migration has already been performed
$migrationCompleted = $util->getMigrationStatus(); $migrationCompleted = $util->getMigrationStatus();
// If migration not yet done // If migration not yet done
if ( ! $migrationCompleted ) { if (!$migrationCompleted) {
$userView = new \OC_FilesystemView( '/' . $params['uid'] ); $userView = new \OC_FilesystemView('/' . $params['uid']);
// Set legacy encryption key if it exists, to support // Set legacy encryption key if it exists, to support
// depreciated encryption system // depreciated encryption system
if ( if (
$userView->file_exists( 'encryption.key' ) $userView->file_exists('encryption.key')
&& $encLegacyKey = $userView->file_get_contents( 'encryption.key' ) && $encLegacyKey = $userView->file_get_contents('encryption.key')
) { ) {
$plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] ); $plainLegacyKey = Crypt::legacyDecrypt($encLegacyKey, $params['password']);
$session->setLegacyKey( $plainLegacyKey ); $session->setLegacyKey($plainLegacyKey);
} }
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] ); $publicKey = Keymanager::getPublicKey($view, $params['uid']);
// Encrypt existing user files: // Encrypt existing user files:
// This serves to upgrade old versions of the encryption // This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt) // app (see appinfo/spec.txt)
if ( if (
$util->encryptAll( '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] ) $util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'])
) { ) {
\OC_Log::write( \OC_Log::write(
@ -99,7 +99,7 @@ class Hooks {
} }
// Register successful migration in DB // Register successful migration in DB
$util->setMigrationStatus( 1 ); $util->setMigrationStatus(1);
} }
@ -111,10 +111,10 @@ class Hooks {
* @brief setup encryption backend upon user created * @brief setup encryption backend upon user created
* @note This method should never be called for users using client side encryption * @note This method should never be called for users using client side encryption
*/ */
public static function postCreateUser( $params ) { public static function postCreateUser($params) {
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $params['uid'] ); $util = new Util($view, $params['uid']);
Helper::setupUser($util, $params['password']); Helper::setupUser($util, $params['password']);
} }
@ -123,8 +123,8 @@ class Hooks {
* @brief cleanup encryption backend upon user deleted * @brief cleanup encryption backend upon user deleted
* @note This method should never be called for users using client side encryption * @note This method should never be called for users using client side encryption
*/ */
public static function postDeleteUser( $params ) { public static function postDeleteUser($params) {
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
// cleanup public key // cleanup public key
$publicKey = '/public-keys/' . $params['uid'] . '.public.key'; $publicKey = '/public-keys/' . $params['uid'] . '.public.key';
@ -187,15 +187,16 @@ class Hooks {
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Save public key // Save public key
$view->file_put_contents( '/public-keys/'.$user.'.public.key', $keypair['publicKey'] ); $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
// Encrypt private key empty passphrase // Encrypt private key empty passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $newUserPassword ); $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
// Save private key // Save private key
$view->file_put_contents( '/'.$user.'/files_encryption/'.$user.'.private.key', $encryptedPrivateKey ); $view->file_put_contents(
'/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey);
if ( $recoveryPassword ) { // if recovery key is set we can re-encrypt the key files if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
$util = new Util($view, $user); $util = new Util($view, $user);
$util->recoverUsersFiles($recoveryPassword); $util->recoverUsersFiles($recoveryPassword);
} }
@ -233,10 +234,11 @@ class Hooks {
} }
} }
if($error) if ($error) // Set flag var 'run' to notify emitting
// Set flag var 'run' to notify emitting
// script that hook execution failed // script that hook execution failed
{
$params['run']->run = false; $params['run']->run = false;
}
// TODO: Make sure files_sharing provides user // TODO: Make sure files_sharing provides user
// feedback on failed share // feedback on failed share
} }
@ -336,7 +338,7 @@ class Hooks {
foreach ($allFiles as $path) { foreach ($allFiles as $path) {
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path); $usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
$util->setSharedFileKeyfiles( $session, $usersSharing, $path ); $util->setSharedFileKeyfiles($session, $usersSharing, $path);
} }
} }
} }
@ -344,7 +346,7 @@ class Hooks {
/** /**
* @brief * @brief
*/ */
public static function postUnshare( $params ) { public static function postUnshare($params) {
// NOTE: $params has keys: // NOTE: $params has keys:
// [itemType] => file // [itemType] => file
@ -353,31 +355,31 @@ class Hooks {
// [shareWith] => test1 // [shareWith] => test1
// [itemParent] => // [itemParent] =>
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) { if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId); $util = new Util($view, $userId);
$path = $util->fileIdToPath( $params['itemSource'] ); $path = $util->fileIdToPath($params['itemSource']);
// check if this is a re-share // check if this is a re-share
if ( $params['itemParent'] ) { if ($params['itemParent']) {
// get the parent from current share // get the parent from current share
$parent = $util->getShareParent( $params['itemParent'] ); $parent = $util->getShareParent($params['itemParent']);
// get target path // get target path
$targetPath = $util->fileIdToPath( $params['itemSource'] ); $targetPath = $util->fileIdToPath($params['itemSource']);
$targetPathSplit = array_reverse( explode( '/', $targetPath ) ); $targetPathSplit = array_reverse(explode('/', $targetPath));
// init values // init values
$path = ''; $path = '';
$sharedPart = ltrim( $parent['file_target'], '/' ); $sharedPart = ltrim($parent['file_target'], '/');
// rebuild path // rebuild path
foreach ( $targetPathSplit as $pathPart ) { foreach ($targetPathSplit as $pathPart) {
if ( $pathPart !== $sharedPart ) { if ($pathPart !== $sharedPart) {
$path = '/' . $pathPart . $path; $path = '/' . $pathPart . $path;
@ -394,34 +396,36 @@ class Hooks {
} }
// for group shares get a list of the group members // for group shares get a list of the group members
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) { if ($params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP) {
$userIds = \OC_Group::usersInGroup($params['shareWith']); $userIds = \OC_Group::usersInGroup($params['shareWith']);
} else if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_LINK ){
$userIds = array( $util->getPublicShareKeyId() );
} else { } else {
$userIds = array( $params['shareWith'] ); if ($params['shareType'] == \OCP\Share::SHARE_TYPE_LINK) {
$userIds = array($util->getPublicShareKeyId());
} else {
$userIds = array($params['shareWith']);
}
} }
// if we unshare a folder we need a list of all (sub-)files // if we unshare a folder we need a list of all (sub-)files
if ( $params['itemType'] === 'folder' ) { if ($params['itemType'] === 'folder') {
$allFiles = $util->getAllFiles( $path ); $allFiles = $util->getAllFiles($path);
} else { } else {
$allFiles = array( $path ); $allFiles = array($path);
} }
foreach ( $allFiles as $path ) { foreach ($allFiles as $path) {
// check if the user still has access to the file, otherwise delete share key // check if the user still has access to the file, otherwise delete share key
$sharingUsers = $util->getSharingUsersArray( true, $path ); $sharingUsers = $util->getSharingUsersArray(true, $path);
// Unshare every user who no longer has access to the file // Unshare every user who no longer has access to the file
$delUsers = array_diff( $userIds, $sharingUsers); $delUsers = array_diff($userIds, $sharingUsers);
// delete share key // delete share key
Keymanager::delShareKey( $view, $delUsers, $path ); Keymanager::delShareKey($view, $delUsers, $path);
} }
} }
@ -442,11 +446,13 @@ class Hooks {
$view = new \OC_FilesystemView('/'); $view = new \OC_FilesystemView('/');
$session = new Session($view); $session = new Session($view);
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
// Format paths to be relative to user files dir // Format paths to be relative to user files dir
$oldKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']); $oldKeyfilePath = \OC\Files\Filesystem::normalizePath(
$newKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']); $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']);
$newKeyfilePath = \OC\Files\Filesystem::normalizePath(
$userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']);
// add key ext if this is not an folder // add key ext if this is not an folder
if (!$view->is_dir($oldKeyfilePath)) { if (!$view->is_dir($oldKeyfilePath)) {
@ -454,13 +460,13 @@ class Hooks {
$newKeyfilePath .= '.key'; $newKeyfilePath .= '.key';
// handle share-keys // handle share-keys
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$params['oldpath']); $localKeyPath = $view->getLocalFile($userId . '/files_encryption/share-keys/' . $params['oldpath']);
$matches = glob(preg_quote($localKeyPath).'*.shareKey'); $matches = glob(preg_quote($localKeyPath) . '*.shareKey');
foreach ($matches as $src) { foreach ($matches as $src) {
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src)); $dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
// create destination folder if not exists // create destination folder if not exists
if(!file_exists(dirname($dst))) { if (!file_exists(dirname($dst))) {
mkdir(dirname($dst), 0750, true); mkdir(dirname($dst), 0750, true);
} }
@ -469,11 +475,13 @@ class Hooks {
} else { } else {
// handle share-keys folders // handle share-keys folders
$oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']); $oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath(
$newShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']); $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']);
$newShareKeyfilePath = \OC\Files\Filesystem::normalizePath(
$userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']);
// create destination folder if not exists // create destination folder if not exists
if(!$view->file_exists(dirname($newShareKeyfilePath))) { if (!$view->file_exists(dirname($newShareKeyfilePath))) {
$view->mkdir(dirname($newShareKeyfilePath), 0750, true); $view->mkdir(dirname($newShareKeyfilePath), 0750, true);
} }
@ -481,10 +489,10 @@ class Hooks {
} }
// Rename keyfile so it isn't orphaned // Rename keyfile so it isn't orphaned
if($view->file_exists($oldKeyfilePath)) { if ($view->file_exists($oldKeyfilePath)) {
// create destination folder if not exists // create destination folder if not exists
if(!$view->file_exists(dirname($newKeyfilePath))) { if (!$view->file_exists(dirname($newKeyfilePath))) {
$view->mkdir(dirname($newKeyfilePath), 0750, true); $view->mkdir(dirname($newKeyfilePath), 0750, true);
} }
@ -492,10 +500,10 @@ class Hooks {
} }
// build the path to the file // build the path to the file
$newPath = '/' . $userId . '/files' .$params['newpath']; $newPath = '/' . $userId . '/files' . $params['newpath'];
$newPathRelative = $params['newpath']; $newPathRelative = $params['newpath'];
if($util->fixFileSize($newPath)) { if ($util->fixFileSize($newPath)) {
// get sharing app state // get sharing app state
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();

View File

@ -26,21 +26,20 @@
namespace OCA\Encryption; namespace OCA\Encryption;
//require_once '../3rdparty/Crypt_Blowfish/Blowfish.php'; //require_once '../3rdparty/Crypt_Blowfish/Blowfish.php';
require_once realpath( dirname( __FILE__ ) . '/../3rdparty/Crypt_Blowfish/Blowfish.php' ); require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php');
/** /**
* Class for common cryptography functionality * Class for common cryptography functionality
*/ */
class Crypt class Crypt {
{
/** /**
* @brief return encryption mode client or server side encryption * @brief return encryption mode client or server side encryption
* @param string $user name (use system wide setting if name=null) * @param string $user name (use system wide setting if name=null)
* @return string 'client' or 'server' * @return string 'client' or 'server'
*/ */
public static function mode( $user = null ) { public static function mode($user = null) {
return 'server'; return 'server';
@ -52,17 +51,20 @@ class Crypt
*/ */
public static function createKeypair() { public static function createKeypair() {
$res = openssl_pkey_new( array( 'private_key_bits' => 4096 ) ); $res = openssl_pkey_new(array('private_key_bits' => 4096));
// Get private key // Get private key
openssl_pkey_export( $res, $privateKey ); openssl_pkey_export($res, $privateKey);
// Get public key // Get public key
$publicKey = openssl_pkey_get_details( $res ); $publicKey = openssl_pkey_get_details($res);
$publicKey = $publicKey['key']; $publicKey = $publicKey['key'];
return ( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) ); return (array(
'publicKey' => $publicKey,
'privateKey' => $privateKey
));
} }
@ -75,7 +77,7 @@ class Crypt
* blocks with encryption alone, hence padding is added to achieve the * blocks with encryption alone, hence padding is added to achieve the
* required length. * required length.
*/ */
public static function addPadding( $data ) { public static function addPadding($data) {
$padded = $data . 'xx'; $padded = $data . 'xx';
@ -88,11 +90,11 @@ class Crypt
* @param string $padded padded data to remove padding from * @param string $padded padded data to remove padding from
* @return string unpadded data on success, false on error * @return string unpadded data on success, false on error
*/ */
public static function removePadding( $padded ) { public static function removePadding($padded) {
if ( substr( $padded, -2 ) == 'xx' ) { if (substr($padded, -2) == 'xx') {
$data = substr( $padded, 0, -2 ); $data = substr($padded, 0, -2);
return $data; return $data;
@ -111,26 +113,26 @@ class Crypt
* @return boolean * @return boolean
* @note see also OCA\Encryption\Util->isEncryptedPath() * @note see also OCA\Encryption\Util->isEncryptedPath()
*/ */
public static function isCatfileContent( $content ) { public static function isCatfileContent($content) {
if ( !$content ) { if (!$content) {
return false; return false;
} }
$noPadding = self::removePadding( $content ); $noPadding = self::removePadding($content);
// Fetch encryption metadata from end of file // Fetch encryption metadata from end of file
$meta = substr( $noPadding, -22 ); $meta = substr($noPadding, -22);
// Fetch IV from end of file // Fetch IV from end of file
$iv = substr( $meta, -16 ); $iv = substr($meta, -16);
// Fetch identifier from start of metadata // Fetch identifier from start of metadata
$identifier = substr( $meta, 0, 6 ); $identifier = substr($meta, 0, 6);
if ( $identifier == '00iv00' ) { if ($identifier == '00iv00') {
return true; return true;
@ -147,15 +149,15 @@ class Crypt
* @param string $path * @param string $path
* @return bool * @return bool
*/ */
public static function isEncryptedMeta( $path ) { public static function isEncryptedMeta($path) {
// TODO: Use DI to get \OC\Files\Filesystem out of here // TODO: Use DI to get \OC\Files\Filesystem out of here
// Fetch all file metadata from DB // Fetch all file metadata from DB
$metadata = \OC\Files\Filesystem::getFileInfo( $path ); $metadata = \OC\Files\Filesystem::getFileInfo($path);
// Return encryption status // Return encryption status
return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted']; return isset($metadata['encrypted']) and ( bool )$metadata['encrypted'];
} }
@ -166,18 +168,18 @@ class Crypt
* e.g. filename or /Docs/filename, NOT admin/files/filename * e.g. filename or /Docs/filename, NOT admin/files/filename
* @return boolean * @return boolean
*/ */
public static function isLegacyEncryptedContent( $data, $relPath ) { public static function isLegacyEncryptedContent($data, $relPath) {
// Fetch all file metadata from DB // Fetch all file metadata from DB
$metadata = \OC\Files\Filesystem::getFileInfo( $relPath, '' ); $metadata = \OC\Files\Filesystem::getFileInfo($relPath, '');
// If a file is flagged with encryption in DB, but isn't a // If a file is flagged with encryption in DB, but isn't a
// valid content + IV combination, it's probably using the // valid content + IV combination, it's probably using the
// legacy encryption system // legacy encryption system
if ( if (
isset( $metadata['encrypted'] ) isset($metadata['encrypted'])
and $metadata['encrypted'] === true and $metadata['encrypted'] === true
and !self::isCatfileContent( $data ) and !self::isCatfileContent($data)
) { ) {
return true; return true;
@ -197,15 +199,15 @@ class Crypt
* @param string $passphrase * @param string $passphrase
* @return string encrypted file content * @return string encrypted file content
*/ */
public static function encrypt( $plainContent, $iv, $passphrase = '' ) { public static function encrypt($plainContent, $iv, $passphrase = '') {
if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { if ($encryptedContent = openssl_encrypt($plainContent, 'AES-128-CFB', $passphrase, false, $iv)) {
return $encryptedContent; return $encryptedContent;
} else { } else {
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR);
return false; return false;
@ -221,15 +223,15 @@ class Crypt
* @throws \Exception * @throws \Exception
* @return string decrypted file content * @return string decrypted file content
*/ */
public static function decrypt( $encryptedContent, $iv, $passphrase ) { public static function decrypt($encryptedContent, $iv, $passphrase) {
if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { if ($plainContent = openssl_decrypt($encryptedContent, 'AES-128-CFB', $passphrase, false, $iv)) {
return $plainContent; return $plainContent;
} else { } else {
throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' ); throw new \Exception('Encryption library: Decryption (symmetric) of content failed');
} }
@ -241,7 +243,7 @@ class Crypt
* @param string $iv IV to be concatenated * @param string $iv IV to be concatenated
* @returns string concatenated content * @returns string concatenated content
*/ */
public static function concatIv( $content, $iv ) { public static function concatIv($content, $iv) {
$combined = $content . '00iv00' . $iv; $combined = $content . '00iv00' . $iv;
@ -254,20 +256,21 @@ class Crypt
* @param string $catFile concatenated data to be split * @param string $catFile concatenated data to be split
* @returns array keys: encrypted, iv * @returns array keys: encrypted, iv
*/ */
public static function splitIv( $catFile ) { public static function splitIv($catFile) {
// Fetch encryption metadata from end of file // Fetch encryption metadata from end of file
$meta = substr( $catFile, -22 ); $meta = substr($catFile, -22);
// Fetch IV from end of file // Fetch IV from end of file
$iv = substr( $meta, -16 ); $iv = substr($meta, -16);
// Remove IV and IV identifier text to expose encrypted content // Remove IV and IV identifier text to expose encrypted content
$encrypted = substr( $catFile, 0, -22 ); $encrypted = substr($catFile, 0, -22);
$split = array( $split = array(
'encrypted' => $encrypted 'encrypted' => $encrypted
, 'iv' => $iv ,
'iv' => $iv
); );
return $split; return $split;
@ -283,9 +286,9 @@ class Crypt
* @note IV need not be specified, as it will be stored in the returned keyfile * @note IV need not be specified, as it will be stored in the returned keyfile
* and remain accessible therein. * and remain accessible therein.
*/ */
public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) { public static function symmetricEncryptFileContent($plainContent, $passphrase = '') {
if ( !$plainContent ) { if (!$plainContent) {
return false; return false;
@ -293,18 +296,18 @@ class Crypt
$iv = self::generateIv(); $iv = self::generateIv();
if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { if ($encryptedContent = self::encrypt($plainContent, $iv, $passphrase)) {
// Combine content to encrypt with IV identifier and actual IV // Combine content to encrypt with IV identifier and actual IV
$catfile = self::concatIv( $encryptedContent, $iv ); $catfile = self::concatIv($encryptedContent, $iv);
$padded = self::addPadding( $catfile ); $padded = self::addPadding($catfile);
return $padded; return $padded;
} else { } else {
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR);
return false; return false;
@ -326,21 +329,21 @@ class Crypt
* *
* This function decrypts a file * This function decrypts a file
*/ */
public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) { public static function symmetricDecryptFileContent($keyfileContent, $passphrase = '') {
if ( !$keyfileContent ) { if (!$keyfileContent) {
throw new \Exception( 'Encryption library: no data provided for decryption' ); throw new \Exception('Encryption library: no data provided for decryption');
} }
// Remove padding // Remove padding
$noPadding = self::removePadding( $keyfileContent ); $noPadding = self::removePadding($keyfileContent);
// Split into enc data and catfile // Split into enc data and catfile
$catfile = self::splitIv( $noPadding ); $catfile = self::splitIv($noPadding);
if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { if ($plainContent = self::decrypt($catfile['encrypted'], $catfile['iv'], $passphrase)) {
return $plainContent; return $plainContent;
@ -358,11 +361,11 @@ class Crypt
* *
* This function decrypts a file * This function decrypts a file
*/ */
public static function symmetricEncryptFileContentKeyfile( $plainContent ) { public static function symmetricEncryptFileContentKeyfile($plainContent) {
$key = self::generateKey(); $key = self::generateKey();
if ( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) { if ($encryptedContent = self::symmetricEncryptFileContent($plainContent, $key)) {
return array( return array(
'key' => $key, 'key' => $key,
@ -384,13 +387,13 @@ class Crypt
* @returns array keys: keys (array, key = userId), data * @returns array keys: keys (array, key = userId), data
* @note symmetricDecryptFileContent() can decrypt files created using this method * @note symmetricDecryptFileContent() can decrypt files created using this method
*/ */
public static function multiKeyEncrypt( $plainContent, array $publicKeys ) { public static function multiKeyEncrypt($plainContent, array $publicKeys) {
// openssl_seal returns false without errors if $plainContent // openssl_seal returns false without errors if $plainContent
// is empty, so trigger our own error // is empty, so trigger our own error
if ( empty( $plainContent ) ) { if (empty($plainContent)) {
throw new \Exception( 'Cannot mutliKeyEncrypt empty plain content' ); throw new \Exception('Cannot mutliKeyEncrypt empty plain content');
} }
@ -399,13 +402,13 @@ class Crypt
$shareKeys = array(); $shareKeys = array();
$mappedShareKeys = array(); $mappedShareKeys = array();
if ( openssl_seal( $plainContent, $sealed, $shareKeys, $publicKeys ) ) { if (openssl_seal($plainContent, $sealed, $shareKeys, $publicKeys)) {
$i = 0; $i = 0;
// Ensure each shareKey is labelled with its // Ensure each shareKey is labelled with its
// corresponding userId // corresponding userId
foreach ( $publicKeys as $userId => $publicKey ) { foreach ($publicKeys as $userId => $publicKey) {
$mappedShareKeys[$userId] = $shareKeys[$i]; $mappedShareKeys[$userId] = $shareKeys[$i];
$i++; $i++;
@ -437,21 +440,21 @@ class Crypt
* *
* This function decrypts a file * This function decrypts a file
*/ */
public static function multiKeyDecrypt( $encryptedContent, $shareKey, $privateKey ) { public static function multiKeyDecrypt($encryptedContent, $shareKey, $privateKey) {
if ( !$encryptedContent ) { if (!$encryptedContent) {
return false; return false;
} }
if ( openssl_open( $encryptedContent, $plainContent, $shareKey, $privateKey ) ) { if (openssl_open($encryptedContent, $plainContent, $shareKey, $privateKey)) {
return $plainContent; return $plainContent;
} else { } else {
\OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR);
return false; return false;
@ -463,9 +466,9 @@ class Crypt
* @brief Asymetrically encrypt a string using a public key * @brief Asymetrically encrypt a string using a public key
* @return string encrypted file * @return string encrypted file
*/ */
public static function keyEncrypt( $plainContent, $publicKey ) { public static function keyEncrypt($plainContent, $publicKey) {
openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey ); openssl_public_encrypt($plainContent, $encryptedContent, $publicKey);
return $encryptedContent; return $encryptedContent;
@ -475,11 +478,11 @@ class Crypt
* @brief Asymetrically decrypt a file using a private key * @brief Asymetrically decrypt a file using a private key
* @return string decrypted file * @return string decrypted file
*/ */
public static function keyDecrypt( $encryptedContent, $privatekey ) { public static function keyDecrypt($encryptedContent, $privatekey) {
$result = @openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey ); $result = @openssl_private_decrypt($encryptedContent, $plainContent, $privatekey);
if ( $result ) { if ($result) {
return $plainContent; return $plainContent;
} }
@ -493,24 +496,24 @@ class Crypt
*/ */
public static function generateIv() { public static function generateIv() {
if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) { if ($random = openssl_random_pseudo_bytes(12, $strong)) {
if ( !$strong ) { if (!$strong) {
// If OpenSSL indicates randomness is insecure, log error // If OpenSSL indicates randomness is insecure, log error
\OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN ); \OC_Log::write('Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN);
} }
// We encode the iv purely for string manipulation // We encode the iv purely for string manipulation
// purposes - it gets decoded before use // purposes - it gets decoded before use
$iv = base64_encode( $random ); $iv = base64_encode($random);
return $iv; return $iv;
} else { } else {
throw new \Exception( 'Generating IV failed' ); throw new \Exception('Generating IV failed');
} }
@ -523,12 +526,12 @@ class Crypt
public static function generateKey() { public static function generateKey() {
// Generate key // Generate key
if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { if ($key = base64_encode(openssl_random_pseudo_bytes(183, $strong))) {
if ( !$strong ) { if (!$strong) {
// If OpenSSL indicates randomness is insecure, log error // If OpenSSL indicates randomness is insecure, log error
throw new \Exception( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' ); throw new \Exception('Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()');
} }
@ -549,11 +552,11 @@ class Crypt
* *
* if the key is left out, the default handeler will be used * if the key is left out, the default handeler will be used
*/ */
public static function getBlowfish( $key = '' ) { public static function getBlowfish($key = '') {
if ( $key ) { if ($key) {
return new \Crypt_Blowfish( $key ); return new \Crypt_Blowfish($key);
} else { } else {
@ -567,13 +570,13 @@ class Crypt
* @param $passphrase * @param $passphrase
* @return mixed * @return mixed
*/ */
public static function legacyCreateKey( $passphrase ) { public static function legacyCreateKey($passphrase) {
// Generate a random integer // Generate a random integer
$key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); $key = mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999);
// Encrypt the key with the passphrase // Encrypt the key with the passphrase
$legacyEncKey = self::legacyEncrypt( $key, $passphrase ); $legacyEncKey = self::legacyEncrypt($key, $passphrase);
return $legacyEncKey; return $legacyEncKey;
@ -589,11 +592,11 @@ class Crypt
* *
* This function encrypts an content * This function encrypts an content
*/ */
public static function legacyEncrypt( $content, $passphrase = '' ) { public static function legacyEncrypt($content, $passphrase = '') {
$bf = self::getBlowfish( $passphrase ); $bf = self::getBlowfish($passphrase);
return $bf->encrypt( $content ); return $bf->encrypt($content);
} }
@ -607,13 +610,13 @@ class Crypt
* *
* This function decrypts an content * This function decrypts an content
*/ */
public static function legacyDecrypt( $content, $passphrase = '' ) { public static function legacyDecrypt($content, $passphrase = '') {
$bf = self::getBlowfish( $passphrase ); $bf = self::getBlowfish($passphrase);
$decrypted = $bf->decrypt( $content ); $decrypted = $bf->decrypt($content);
return rtrim( $decrypted, "\0" );; return rtrim($decrypted, "\0");;
} }
@ -623,16 +626,16 @@ class Crypt
* @param int $maxLength * @param int $maxLength
* @return string * @return string
*/ */
private static function legacyBlockDecrypt( $data, $key = '', $maxLength = 0 ) { private static function legacyBlockDecrypt($data, $key = '', $maxLength = 0) {
$result = ''; $result = '';
while ( strlen( $data ) ) { while (strlen($data)) {
$result .= self::legacyDecrypt( substr( $data, 0, 8192 ), $key ); $result .= self::legacyDecrypt(substr($data, 0, 8192), $key);
$data = substr( $data, 8192 ); $data = substr($data, 8192);
} }
if ( $maxLength > 0 ) { if ($maxLength > 0) {
return substr( $result, 0, $maxLength ); return substr($result, 0, $maxLength);
} else { } else {
return rtrim( $result, "\0" ); return rtrim($result, "\0");
} }
} }
@ -642,17 +645,21 @@ class Crypt
* @param $publicKeys * @param $publicKeys
* @return array * @return array
*/ */
public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKeys ) { public static function legacyKeyRecryptKeyfile($legacyEncryptedContent, $legacyPassphrase, $publicKeys) {
$decrypted = self::legacyBlockDecrypt( $legacyEncryptedContent, $legacyPassphrase ); $decrypted = self::legacyBlockDecrypt($legacyEncryptedContent, $legacyPassphrase);
// Encrypt plain data, generate keyfile & encrypted file // Encrypt plain data, generate keyfile & encrypted file
$cryptedData = self::symmetricEncryptFileContentKeyfile( $decrypted ); $cryptedData = self::symmetricEncryptFileContentKeyfile($decrypted);
// Encrypt plain keyfile to multiple sharefiles // Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $cryptedData['key'], $publicKeys ); $multiEncrypted = Crypt::multiKeyEncrypt($cryptedData['key'], $publicKeys);
return array( 'data' => $cryptedData['encrypted'], 'filekey' => $multiEncrypted['data'], 'sharekeys' => $multiEncrypted['keys'] ); return array(
'data' => $cryptedData['encrypted'],
'filekey' => $multiEncrypted['data'],
'sharekeys' => $multiEncrypted['keys']
);
} }

View File

@ -30,8 +30,7 @@ namespace OCA\Encryption;
* Class Helper * Class Helper
* @package OCA\Encryption * @package OCA\Encryption
*/ */
class Helper class Helper {
{
/** /**
* @brief register share related hooks * @brief register share related hooks
@ -39,9 +38,9 @@ class Helper
*/ */
public static function registerShareHooks() { public static function registerShareHooks() {
\OCP\Util::connectHook( 'OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared' ); \OCP\Util::connectHook('OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared');
\OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' ); \OCP\Util::connectHook('OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared');
\OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' ); \OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare');
} }
/** /**
@ -50,10 +49,10 @@ class Helper
*/ */
public static function registerUserHooks() { public static function registerUserHooks() {
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); \OCP\Util::connectHook('OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login');
\OCP\Util::connectHook( 'OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' ); \OCP\Util::connectHook('OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase');
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' ); \OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser');
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' ); \OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser');
} }
/** /**
@ -62,7 +61,7 @@ class Helper
*/ */
public static function registerFilesystemHooks() { public static function registerFilesystemHooks() {
\OCP\Util::connectHook( 'OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename' ); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
} }
/** /**
@ -72,13 +71,14 @@ class Helper
* @param string $password * @param string $password
* @return bool * @return bool
*/ */
public static function setupUser( $util, $password ) { public static function setupUser($util, $password) {
// Check files_encryption infrastructure is ready for action // Check files_encryption infrastructure is ready for action
if ( !$util->ready() ) { if (!$util->ready()) {
\OC_Log::write( 'Encryption library', 'User account "' . $util->getUserId() . '" is not ready for encryption; configuration started', \OC_Log::DEBUG ); \OC_Log::write('Encryption library', 'User account "' . $util->getUserId()
. '" is not ready for encryption; configuration started', \OC_Log::DEBUG);
if ( !$util->setupServerSide( $password ) ) { if (!$util->setupServerSide($password)) {
return false; return false;
} }
} }
@ -95,21 +95,21 @@ class Helper
* @internal param string $password * @internal param string $password
* @return bool * @return bool
*/ */
public static function adminEnableRecovery( $recoveryKeyId, $recoveryPassword ) { public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) {
$view = new \OC\Files\View( '/' ); $view = new \OC\Files\View('/');
if ( $recoveryKeyId === null ) { if ($recoveryKeyId === null) {
$recoveryKeyId = 'recovery_' . substr( md5( time() ), 0, 8 ); $recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8);
\OC_Appconfig::setValue( 'files_encryption', 'recoveryKeyId', $recoveryKeyId ); \OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId);
} }
if ( !$view->is_dir( '/owncloud_private_key' ) ) { if (!$view->is_dir('/owncloud_private_key')) {
$view->mkdir( '/owncloud_private_key' ); $view->mkdir('/owncloud_private_key');
} }
if ( if (
( !$view->file_exists( "/public-keys/" . $recoveryKeyId . ".public.key" ) (!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key")
|| !$view->file_exists( "/owncloud_private_key/" . $recoveryKeyId . ".private.key" ) ) || !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key"))
) { ) {
$keypair = \OCA\Encryption\Crypt::createKeypair(); $keypair = \OCA\Encryption\Crypt::createKeypair();
@ -118,37 +118,37 @@ class Helper
// Save public key // Save public key
if ( !$view->is_dir( '/public-keys' ) ) { if (!$view->is_dir('/public-keys')) {
$view->mkdir( '/public-keys' ); $view->mkdir('/public-keys');
} }
$view->file_put_contents( '/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey'] ); $view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
// Encrypt private key empthy passphrase // Encrypt private key empthy passphrase
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $recoveryPassword ); $encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword);
// Save private key // Save private key
$view->file_put_contents( '/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey ); $view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
// create control file which let us check later on if the entered password was correct. // create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt( "ownCloud", $keypair['publicKey'] ); $encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']);
if ( !$view->is_dir( '/control-file' ) ) { if (!$view->is_dir('/control-file')) {
$view->mkdir( '/control-file' ); $view->mkdir('/control-file');
} }
$view->file_put_contents( '/control-file/controlfile.enc', $encryptedControlData ); $view->file_put_contents('/control-file/controlfile.enc', $encryptedControlData);
\OC_FileProxy::$enabled = true; \OC_FileProxy::$enabled = true;
// Set recoveryAdmin as enabled // Set recoveryAdmin as enabled
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 1 ); \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
$return = true; $return = true;
} else { // get recovery key and check the password } else { // get recovery key and check the password
$util = new \OCA\Encryption\Util( new \OC_FilesystemView( '/' ), \OCP\User::getUser() ); $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword( $recoveryPassword ); $return = $util->checkRecoveryPassword($recoveryPassword);
if ( $return ) { if ($return) {
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 1 ); \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1);
} }
} }
@ -162,13 +162,13 @@ class Helper
* @param $recoveryPassword * @param $recoveryPassword
* @return bool * @return bool
*/ */
public static function adminDisableRecovery( $recoveryPassword ) { public static function adminDisableRecovery($recoveryPassword) {
$util = new Util( new \OC_FilesystemView( '/' ), \OCP\User::getUser() ); $util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword( $recoveryPassword ); $return = $util->checkRecoveryPassword($recoveryPassword);
if ( $return ) { if ($return) {
// Set recoveryAdmin as disabled // Set recoveryAdmin as disabled
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 0 ); \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0);
} }
return $return; return $return;

View File

@ -27,8 +27,7 @@ namespace OCA\Encryption;
* @brief Class to manage storage and retrieval of encryption keys * @brief Class to manage storage and retrieval of encryption keys
* @note Where a method requires a view object, it's root must be '/' * @note Where a method requires a view object, it's root must be '/'
*/ */
class Keymanager class Keymanager {
{
/** /**
* @brief retrieve the ENCRYPTED private key from a user * @brief retrieve the ENCRYPTED private key from a user
@ -38,14 +37,14 @@ class Keymanager
* @return string private key or false (hopefully) * @return string private key or false (hopefully)
* @note the key returned by this method must be decrypted before use * @note the key returned by this method must be decrypted before use
*/ */
public static function getPrivateKey( \OC_FilesystemView $view, $user ) { public static function getPrivateKey(\OC_FilesystemView $view, $user) {
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key'; $path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key';
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$key = $view->file_get_contents( $path ); $key = $view->file_get_contents($path);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -58,12 +57,12 @@ class Keymanager
* @param $userId * @param $userId
* @return string public key or false * @return string public key or false
*/ */
public static function getPublicKey( \OC_FilesystemView $view, $userId ) { public static function getPublicKey(\OC_FilesystemView $view, $userId) {
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$result = $view->file_get_contents( '/public-keys/' . $userId . '.public.key' ); $result = $view->file_get_contents('/public-keys/' . $userId . '.public.key');
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -77,11 +76,12 @@ class Keymanager
* @param $userId * @param $userId
* @return array keys: privateKey, publicKey * @return array keys: privateKey, publicKey
*/ */
public static function getUserKeys( \OC_FilesystemView $view, $userId ) { public static function getUserKeys(\OC_FilesystemView $view, $userId) {
return array( return array(
'publicKey' => self::getPublicKey( $view, $userId ) 'publicKey' => self::getPublicKey($view, $userId)
, 'privateKey' => self::getPrivateKey( $view, $userId ) ,
'privateKey' => self::getPrivateKey($view, $userId)
); );
} }
@ -92,13 +92,13 @@ class Keymanager
* @param array $userIds * @param array $userIds
* @return array of public keys for the specified users * @return array of public keys for the specified users
*/ */
public static function getPublicKeys( \OC_FilesystemView $view, array $userIds ) { public static function getPublicKeys(\OC_FilesystemView $view, array $userIds) {
$keys = array(); $keys = array();
foreach ( $userIds as $userId ) { foreach ($userIds as $userId) {
$keys[$userId] = self::getPublicKey( $view, $userId ); $keys[$userId] = self::getPublicKey($view, $userId);
} }
@ -118,40 +118,41 @@ class Keymanager
* @note The keyfile is not encrypted here. Client code must * @note The keyfile is not encrypted here. Client code must
* asymmetrically encrypt the keyfile before passing it to this method * asymmetrically encrypt the keyfile before passing it to this method
*/ */
public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) { public static function setFileKey(\OC_FilesystemView $view, $path, $userId, $catfile) {
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $path ); list($owner, $filename) = $util->getUidAndFilename($path);
$basePath = '/' . $owner . '/files_encryption/keyfiles'; $basePath = '/' . $owner . '/files_encryption/keyfiles';
$targetPath = self::keySetPreparation( $view, $filename, $basePath, $owner ); $targetPath = self::keySetPreparation($view, $filename, $basePath, $owner);
if ( !$view->is_dir( $basePath . '/' . $targetPath ) ) { if (!$view->is_dir($basePath . '/' . $targetPath)) {
// create all parent folders // create all parent folders
$info = pathinfo( $basePath . '/' . $targetPath ); $info = pathinfo($basePath . '/' . $targetPath);
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] ); $keyfileFolderName = $view->getLocalFolder($info['dirname']);
if ( !file_exists( $keyfileFolderName ) ) { if (!file_exists($keyfileFolderName)) {
mkdir( $keyfileFolderName, 0750, true ); mkdir($keyfileFolderName, 0750, true);
} }
} }
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $targetPath ) ) { if (self::isPartialFilePath($targetPath)) {
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile ); $result = $view->file_put_contents(
$basePath . '/' . self::fixPartialFilePath($targetPath) . '.key', $catfile);
} else { } else {
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); $result = $view->file_put_contents($basePath . '/' . $targetPath . '.key', $catfile);
} }
@ -167,12 +168,12 @@ class Keymanager
* @return string File path without .part extension * @return string File path without .part extension
* @note this is needed for reusing keys * @note this is needed for reusing keys
*/ */
public static function fixPartialFilePath( $path ) { public static function fixPartialFilePath($path) {
if ( preg_match( '/\.part$/', $path ) ) { if (preg_match('/\.part$/', $path)) {
$newLength = strlen( $path ) - 5; $newLength = strlen($path) - 5;
$fPath = substr( $path, 0, $newLength ); $fPath = substr($path, 0, $newLength);
return $fPath; return $fPath;
@ -189,9 +190,9 @@ class Keymanager
* @param string $path Path that may identify a .part file * @param string $path Path that may identify a .part file
* @return bool * @return bool
*/ */
public static function isPartialFilePath( $path ) { public static function isPartialFilePath($path) {
if ( preg_match( '/\.part$/', $path ) ) { if (preg_match('/\.part$/', $path)) {
return true; return true;
@ -213,14 +214,14 @@ class Keymanager
* @note The keyfile returned is asymmetrically encrypted. Decryption * @note The keyfile returned is asymmetrically encrypted. Decryption
* of the keyfile must be performed by client code * of the keyfile must be performed by client code
*/ */
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getFileKey(\OC_FilesystemView $view, $userId, $filePath) {
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) { if (self::isPartialFilePath($filePath)) {
$result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) ); $result = self::getFileKey($view, $userId, self::fixPartialFilePath($filePath));
if ( $result ) { if ($result) {
return $result; return $result;
@ -228,19 +229,19 @@ class Keymanager
} }
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $filePath ); list($owner, $filename) = $util->getUidAndFilename($filePath);
$filePath_f = ltrim( $filename, '/' ); $filePath_f = ltrim($filename, '/');
$keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key'; $keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key';
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( $view->file_exists( $keyfilePath ) ) { if ($view->file_exists($keyfilePath)) {
$result = $view->file_get_contents( $keyfilePath ); $result = $view->file_get_contents($keyfilePath);
} else { } else {
@ -264,26 +265,29 @@ class Keymanager
* @note $path must be relative to data/user/files. e.g. mydoc.txt NOT * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT
* /data/admin/files/mydoc.txt * /data/admin/files/mydoc.txt
*/ */
public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) { public static function deleteFileKey(\OC_FilesystemView $view, $userId, $path) {
$trimmed = ltrim( $path, '/' ); $trimmed = ltrim($path, '/');
$keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed; $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed;
$result = false; $result = false;
if ( $view->is_dir( $keyPath ) ) { if ($view->is_dir($keyPath)) {
$result = $view->unlink( $keyPath ); $result = $view->unlink($keyPath);
} else if ( $view->file_exists( $keyPath . '.key' ) ) { } else {
if ($view->file_exists($keyPath . '.key')) {
$result = $view->unlink( $keyPath . '.key' ); $result = $view->unlink($keyPath . '.key');
} }
}
if ( !$result ) { if (!$result) {
\OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); \OC_Log::write('Encryption library',
'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR);
} }
@ -298,19 +302,19 @@ class Keymanager
* @note Encryption of the private key must be performed by client code * @note Encryption of the private key must be performed by client code
* as no encryption takes place here * as no encryption takes place here
*/ */
public static function setPrivateKey( $key ) { public static function setPrivateKey($key) {
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); $view = new \OC_FilesystemView('/' . $user . '/files_encryption');
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) ) if (!$view->file_exists(''))
$view->mkdir( '' ); $view->mkdir('');
$result = $view->file_put_contents( $user . '.private.key', $key ); $result = $view->file_put_contents($user . '.private.key', $key);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -331,21 +335,21 @@ class Keymanager
* @note The keyfile is not encrypted here. Client code must * @note The keyfile is not encrypted here. Client code must
* asymmetrically encrypt the keyfile before passing it to this method * asymmetrically encrypt the keyfile before passing it to this method
*/ */
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { public static function setShareKey(\OC_FilesystemView $view, $path, $userId, $shareKey) {
// Here we need the currently logged in user, while userId can be a different user // Here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $path ); list($owner, $filename) = $util->getUidAndFilename($path);
$basePath = '/' . $owner . '/files_encryption/share-keys'; $basePath = '/' . $owner . '/files_encryption/share-keys';
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner ); $shareKeyPath = self::keySetPreparation($view, $filename, $basePath, $owner);
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $shareKeyPath ) ) { if (self::isPartialFilePath($shareKeyPath)) {
$writePath = $basePath . '/' . self::fixPartialFilePath( $shareKeyPath ) . '.' . $userId . '.shareKey'; $writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey';
} else { } else {
@ -356,12 +360,12 @@ class Keymanager
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$result = $view->file_put_contents( $writePath, $shareKey ); $result = $view->file_put_contents($writePath, $shareKey);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( if (
is_int( $result ) is_int($result)
&& $result > 0 && $result > 0
) { ) {
@ -382,16 +386,16 @@ class Keymanager
* @param array $shareKeys * @param array $shareKeys
* @return bool * @return bool
*/ */
public static function setShareKeys( \OC_FilesystemView $view, $path, array $shareKeys ) { public static function setShareKeys(\OC_FilesystemView $view, $path, array $shareKeys) {
// $shareKeys must be an array with the following format: // $shareKeys must be an array with the following format:
// [userId] => [encrypted key] // [userId] => [encrypted key]
$result = true; $result = true;
foreach ( $shareKeys as $userId => $shareKey ) { foreach ($shareKeys as $userId => $shareKey) {
if ( !self::setShareKey( $view, $path, $userId, $shareKey ) ) { if (!self::setShareKey($view, $path, $userId, $shareKey)) {
// If any of the keys are not set, flag false // If any of the keys are not set, flag false
$result = false; $result = false;
@ -415,14 +419,14 @@ class Keymanager
* @note The sharekey returned is encrypted. Decryption * @note The sharekey returned is encrypted. Decryption
* of the keyfile must be performed by client code * of the keyfile must be performed by client code
*/ */
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getShareKey(\OC_FilesystemView $view, $userId, $filePath) {
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) { if (self::isPartialFilePath($filePath)) {
$result = self::getShareKey( $view, $userId, self::fixPartialFilePath( $filePath ) ); $result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath));
if ( $result ) { if ($result) {
return $result; return $result;
@ -434,14 +438,15 @@ class Keymanager
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $filePath ); list($owner, $filename) = $util->getUidAndFilename($filePath);
$shareKeyPath = \OC\Files\Filesystem::normalizePath( '/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey' ); $shareKeyPath = \OC\Files\Filesystem::normalizePath(
'/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey');
if ( $view->file_exists( $shareKeyPath ) ) { if ($view->file_exists($shareKeyPath)) {
$result = $view->file_get_contents( $shareKeyPath ); $result = $view->file_get_contents($shareKeyPath);
} else { } else {
@ -461,17 +466,18 @@ class Keymanager
* @param string $userId owner of the file * @param string $userId owner of the file
* @param string $filePath path to the file, relative to the owners file dir * @param string $filePath path to the file, relative to the owners file dir
*/ */
public static function delAllShareKeys( \OC_FilesystemView $view, $userId, $filePath ) { public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath) {
if ( $view->is_dir( $userId . '/files/' . $filePath ) ) { if ($view->is_dir($userId . '/files/' . $filePath)) {
$view->unlink( $userId . '/files_encryption/share-keys/' . $filePath ); $view->unlink($userId . '/files_encryption/share-keys/' . $filePath);
} else { } else {
$localKeyPath = $view->getLocalFile( $userId . '/files_encryption/share-keys/' . $filePath ); $localKeyPath = $view->getLocalFile($userId . '/files_encryption/share-keys/' . $filePath);
$matches = glob( preg_quote( $localKeyPath ) . '*.shareKey' ); $matches = glob(preg_quote($localKeyPath) . '*.shareKey');
foreach ( $matches as $ma ) { foreach ($matches as $ma) {
$result = unlink( $ma ); $result = unlink($ma);
if ( !$result ) { if (!$result) {
\OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OC_Log::ERROR ); \OC_Log::write('Encryption library',
'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OC_Log::ERROR);
} }
} }
} }
@ -480,29 +486,31 @@ class Keymanager
/** /**
* @brief Delete a single user's shareKey for a single file * @brief Delete a single user's shareKey for a single file
*/ */
public static function delShareKey( \OC_FilesystemView $view, $userIds, $filePath ) { public static function delShareKey(\OC_FilesystemView $view, $userIds, $filePath) {
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $filePath ); list($owner, $filename) = $util->getUidAndFilename($filePath);
$shareKeyPath = \OC\Files\Filesystem::normalizePath( '/' . $owner . '/files_encryption/share-keys/' . $filename ); $shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename);
if ( $view->is_dir( $shareKeyPath ) ) { if ($view->is_dir($shareKeyPath)) {
$localPath = \OC\Files\Filesystem::normalizePath( $view->getLocalFolder( $shareKeyPath ) ); $localPath = \OC\Files\Filesystem::normalizePath($view->getLocalFolder($shareKeyPath));
self::recursiveDelShareKeys( $localPath, $userIds ); self::recursiveDelShareKeys($localPath, $userIds);
} else { } else {
foreach ( $userIds as $userId ) { foreach ($userIds as $userId) {
if ( !$view->unlink( $shareKeyPath . '.' . $userId . '.shareKey' ) ) { if (!$view->unlink($shareKeyPath . '.' . $userId . '.shareKey')) {
\OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId . '.shareKey"', \OC_Log::ERROR ); \OC_Log::write('Encryption library',
'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId
. '.shareKey"', \OC_Log::ERROR);
} }
} }
@ -517,42 +525,43 @@ class Keymanager
* @param string $dir directory * @param string $dir directory
* @param array $userIds user ids for which the share keys should be deleted * @param array $userIds user ids for which the share keys should be deleted
*/ */
private static function recursiveDelShareKeys( $dir, $userIds ) { private static function recursiveDelShareKeys($dir, $userIds) {
foreach ( $userIds as $userId ) { foreach ($userIds as $userId) {
$matches = glob( preg_quote( $dir ) . '/*' . preg_quote( '.' . $userId . '.shareKey' ) ); $matches = glob(preg_quote($dir) . '/*' . preg_quote('.' . $userId . '.shareKey'));
} }
/** @var $matches array */ /** @var $matches array */
foreach ( $matches as $ma ) { foreach ($matches as $ma) {
if ( !unlink( $ma ) ) { if (!unlink($ma)) {
\OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $ma . '"', \OC_Log::ERROR ); \OC_Log::write('Encryption library',
'Could not delete shareKey; does not exist: "' . $ma . '"', \OC_Log::ERROR);
} }
} }
$subdirs = $directories = glob( preg_quote( $dir ) . '/*', GLOB_ONLYDIR ); $subdirs = $directories = glob(preg_quote($dir) . '/*', GLOB_ONLYDIR);
foreach ( $subdirs as $subdir ) { foreach ($subdirs as $subdir) {
self::recursiveDelShareKeys( $subdir, $userIds ); self::recursiveDelShareKeys($subdir, $userIds);
} }
} }
/** /**
* @brief Make preparations to vars and filesystem for saving a keyfile * @brief Make preparations to vars and filesystem for saving a keyfile
*/ */
public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) { public static function keySetPreparation(\OC_FilesystemView $view, $path, $basePath, $userId) {
$targetPath = ltrim( $path, '/' ); $targetPath = ltrim($path, '/');
$path_parts = pathinfo( $targetPath ); $path_parts = pathinfo($targetPath);
// If the file resides within a subdirectory, create it // If the file resides within a subdirectory, create it
if ( if (
isset( $path_parts['dirname'] ) isset($path_parts['dirname'])
&& !$view->file_exists( $basePath . '/' . $path_parts['dirname'] ) && !$view->file_exists($basePath . '/' . $path_parts['dirname'])
) { ) {
$sub_dirs = explode( DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname'] ); $sub_dirs = explode(DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname']);
$dir = ''; $dir = '';
foreach ( $sub_dirs as $sub_dir ) { foreach ($sub_dirs as $sub_dir) {
$dir .= '/' . $sub_dir; $dir .= '/' . $sub_dir;
if ( !$view->is_dir( $dir ) ) { if (!$view->is_dir($dir)) {
$view->mkdir( $dir ); $view->mkdir($dir);
} }
} }
} }

View File

@ -34,8 +34,7 @@ namespace OCA\Encryption;
* Class Proxy * Class Proxy
* @package OCA\Encryption * @package OCA\Encryption
*/ */
class Proxy extends \OC_FileProxy class Proxy extends \OC_FileProxy {
{
private static $blackList = null; //mimetypes blacklisted from encryption private static $blackList = null; //mimetypes blacklisted from encryption
@ -48,12 +47,12 @@ class Proxy extends \OC_FileProxy
* *
* Tests if server side encryption is enabled, and file is allowed by blacklists * Tests if server side encryption is enabled, and file is allowed by blacklists
*/ */
private static function shouldEncrypt( $path ) { private static function shouldEncrypt($path) {
if ( is_null( self::$enableEncryption ) ) { if (is_null(self::$enableEncryption)) {
if ( if (
\OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true' \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') == 'true'
&& Crypt::mode() == 'server' && Crypt::mode() == 'server'
) { ) {
@ -67,27 +66,27 @@ class Proxy extends \OC_FileProxy
} }
if ( !self::$enableEncryption ) { if (!self::$enableEncryption) {
return false; return false;
} }
if ( is_null( self::$blackList ) ) { if (is_null(self::$blackList)) {
self::$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) ); self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', ''));
} }
if ( Crypt::isCatfileContent( $path ) ) { if (Crypt::isCatfileContent($path)) {
return true; return true;
} }
$extension = substr( $path, strrpos( $path, '.' ) + 1 ); $extension = substr($path, strrpos($path, '.') + 1);
if ( array_search( $extension, self::$blackList ) === false ) { if (array_search($extension, self::$blackList) === false) {
return true; return true;
@ -101,34 +100,34 @@ class Proxy extends \OC_FileProxy
* @param $data * @param $data
* @return bool * @return bool
*/ */
public function preFile_put_contents( $path, &$data ) { public function preFile_put_contents($path, &$data) {
if ( self::shouldEncrypt( $path ) ) { if (self::shouldEncrypt($path)) {
// Stream put contents should have been converted to fopen // Stream put contents should have been converted to fopen
if ( !is_resource( $data ) ) { if (!is_resource($data)) {
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$session = new Session( $view ); $session = new Session($view);
$privateKey = $session->getPrivateKey(); $privateKey = $session->getPrivateKey();
$filePath = $util->stripUserFilesPath( $path ); $filePath = $util->stripUserFilesPath($path);
// Set the filesize for userland, before encrypting // Set the filesize for userland, before encrypting
$size = strlen( $data ); $size = strlen($data);
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Check if there is an existing key we can reuse // Check if there is an existing key we can reuse
if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) { if ($encKeyfile = Keymanager::getFileKey($view, $userId, $filePath)) {
// Fetch shareKey // Fetch shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $filePath ); $shareKey = Keymanager::getShareKey($view, $userId, $filePath);
// Decrypt the keyfile // Decrypt the keyfile
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); $plainKey = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
} else { } else {
@ -138,37 +137,41 @@ class Proxy extends \OC_FileProxy
} }
// Encrypt data // Encrypt data
$encData = Crypt::symmetricEncryptFileContent( $data, $plainKey ); $encData = Crypt::symmetricEncryptFileContent($data, $plainKey);
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
// if file exists try to get sharing users // if file exists try to get sharing users
if ( $view->file_exists( $path ) ) { if ($view->file_exists($path)) {
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId ); $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $filePath, $userId);
} else { } else {
$uniqueUserIds[] = $userId; $uniqueUserIds[] = $userId;
} }
// Fetch public keys for all users who will share the file // Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds ); $publicKeys = Keymanager::getPublicKeys($view, $uniqueUserIds);
// Encrypt plain keyfile to multiple sharefiles // Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); $multiEncrypted = Crypt::multiKeyEncrypt($plainKey, $publicKeys);
// Save sharekeys to user folders // Save sharekeys to user folders
Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] ); Keymanager::setShareKeys($view, $filePath, $multiEncrypted['keys']);
// Set encrypted keyfile as common varname // Set encrypted keyfile as common varname
$encKey = $multiEncrypted['data']; $encKey = $multiEncrypted['data'];
// Save keyfile for newly encrypted file in parallel directory tree // Save keyfile for newly encrypted file in parallel directory tree
Keymanager::setFileKey( $view, $filePath, $userId, $encKey ); Keymanager::setFileKey($view, $filePath, $userId, $encKey);
// Replace plain content with encrypted content by reference // Replace plain content with encrypted content by reference
$data = $encData; $data = $encData;
// Update the file cache with file info // Update the file cache with file info
\OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted' => true, 'size' => strlen( $data ), 'unencrypted_size' => $size ), '' ); \OC\Files\Filesystem::putFileInfo($filePath, array(
'encrypted' => true,
'size' => strlen($data),
'unencrypted_size' => $size
), '');
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -184,51 +187,51 @@ class Proxy extends \OC_FileProxy
* @param string $path Path of file from which has been read * @param string $path Path of file from which has been read
* @param string $data Data that has been read from file * @param string $data Data that has been read from file
*/ */
public function postFile_get_contents( $path, $data ) { public function postFile_get_contents($path, $data) {
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$relPath = $util->stripUserFilesPath( $path ); $relPath = $util->stripUserFilesPath($path);
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// init session // init session
$session = new Session( $view ); $session = new Session($view);
// If data is a catfile // If data is a catfile
if ( if (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& Crypt::isCatfileContent( $data ) && Crypt::isCatfileContent($data)
) { ) {
$privateKey = $session->getPrivateKey( $userId ); $privateKey = $session->getPrivateKey($userId);
// Get the encrypted keyfile // Get the encrypted keyfile
$encKeyfile = Keymanager::getFileKey( $view, $userId, $relPath ); $encKeyfile = Keymanager::getFileKey($view, $userId, $relPath);
// Attempt to fetch the user's shareKey // Attempt to fetch the user's shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $relPath ); $shareKey = Keymanager::getShareKey($view, $userId, $relPath);
// Decrypt keyfile with shareKey // Decrypt keyfile with shareKey
$plainKeyfile = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
$plainData = Crypt::symmetricDecryptFileContent( $data, $plainKeyfile ); $plainData = Crypt::symmetricDecryptFileContent($data, $plainKeyfile);
} elseif ( } elseif (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& isset( $_SESSION['legacyenckey'] ) && isset($_SESSION['legacyenckey'])
&& Crypt::isEncryptedMeta( $path ) && Crypt::isEncryptedMeta($path)
) { ) {
$plainData = Crypt::legacyDecrypt( $data, $session->getLegacyKey() ); $plainData = Crypt::legacyDecrypt($data, $session->getLegacyKey());
} }
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( !isset( $plainData ) ) { if (!isset($plainData)) {
$plainData = $data; $plainData = $data;
@ -241,10 +244,10 @@ class Proxy extends \OC_FileProxy
/** /**
* @brief When a file is deleted, remove its keyfile also * @brief When a file is deleted, remove its keyfile also
*/ */
public function preUnlink( $path ) { public function preUnlink($path) {
// let the trashbin handle this // let the trashbin handle this
if ( \OCP\App::isEnabled( 'files_trashbin' ) ) { if (\OCP\App::isEnabled('files_trashbin')) {
return true; return true;
} }
@ -252,23 +255,24 @@ class Proxy extends \OC_FileProxy
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
// Format path to be relative to user files dir // Format path to be relative to user files dir
$relPath = $util->stripUserFilesPath( $path ); $relPath = $util->stripUserFilesPath($path);
list( $owner, $ownerPath ) = $util->getUidAndFilename( $relPath ); list($owner, $ownerPath) = $util->getUidAndFilename($relPath);
// Delete keyfile & shareKey so it isn't orphaned // Delete keyfile & shareKey so it isn't orphaned
if ( !Keymanager::deleteFileKey( $view, $owner, $ownerPath ) ) { if (!Keymanager::deleteFileKey($view, $owner, $ownerPath)) {
\OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OC_Log::ERROR ); \OC_Log::write('Encryption library',
'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OC_Log::ERROR);
} }
Keymanager::delAllShareKeys( $view, $owner, $ownerPath ); Keymanager::delAllShareKeys($view, $owner, $ownerPath);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -282,8 +286,8 @@ class Proxy extends \OC_FileProxy
* @param $path * @param $path
* @return bool * @return bool
*/ */
public function postTouch( $path ) { public function postTouch($path) {
$this->handleFile( $path ); $this->handleFile($path);
return true; return true;
} }
@ -293,20 +297,20 @@ class Proxy extends \OC_FileProxy
* @param $result * @param $result
* @return resource * @return resource
*/ */
public function postFopen( $path, &$result ) { public function postFopen($path, &$result) {
if ( !$result ) { if (!$result) {
return $result; return $result;
} }
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode( '/', $path ); $path_split = explode('/', $path);
$path_f = implode( '/', array_slice( $path_split, 3 ) ); $path_f = implode('/', array_slice($path_split, 3));
// FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
if ( count($path_split) >= 2 && $path_split[2] == 'cache' ) { if (count($path_split) >= 2 && $path_split[2] == 'cache') {
return $result; return $result;
} }
@ -314,31 +318,31 @@ class Proxy extends \OC_FileProxy
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$meta = stream_get_meta_data( $result ); $meta = stream_get_meta_data($result);
$view = new \OC_FilesystemView( '' ); $view = new \OC_FilesystemView('');
$util = new Util( $view, \OCP\USER::getUser() ); $util = new Util($view, \OCP\USER::getUser());
// If file is already encrypted, decrypt using crypto protocol // If file is already encrypted, decrypt using crypto protocol
if ( if (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& $util->isEncryptedPath( $path ) && $util->isEncryptedPath($path)
) { ) {
// Close the original encrypted file // Close the original encrypted file
fclose( $result ); fclose($result);
// Open the file using the crypto stream wrapper // Open the file using the crypto stream wrapper
// protocol and let it do the decryption work instead // protocol and let it do the decryption work instead
$result = fopen( 'crypt://' . $path_f, $meta['mode'] ); $result = fopen('crypt://' . $path_f, $meta['mode']);
} elseif ( } elseif (
self::shouldEncrypt( $path ) self::shouldEncrypt($path)
and $meta ['mode'] != 'r' and $meta ['mode'] != 'r'
and $meta['mode'] != 'rb' and $meta['mode'] != 'rb'
) { ) {
$result = fopen( 'crypt://' . $path_f, $meta['mode'] ); $result = fopen('crypt://' . $path_f, $meta['mode']);
} }
// Re-enable the proxy // Re-enable the proxy
@ -353,17 +357,17 @@ class Proxy extends \OC_FileProxy
* @param $data * @param $data
* @return array * @return array
*/ */
public function postGetFileInfo( $path, $data ) { public function postGetFileInfo($path, $data) {
// if path is a folder do nothing // if path is a folder do nothing
if ( is_array( $data ) && array_key_exists( 'size', $data ) ) { if (is_array($data) && array_key_exists('size', $data)) {
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get file size // get file size
$data['size'] = self::postFileSize( $path, $data['size'] ); $data['size'] = self::postFileSize($path, $data['size']);
// Re-enable the proxy // Re-enable the proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -377,51 +381,51 @@ class Proxy extends \OC_FileProxy
* @param $size * @param $size
* @return bool * @return bool
*/ */
public function postFileSize( $path, $size ) { public function postFileSize($path, $size) {
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
// if path is a folder do nothing // if path is a folder do nothing
if ( $view->is_dir( $path ) ) { if ($view->is_dir($path)) {
return $size; return $size;
} }
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode( '/', $path ); $path_split = explode('/', $path);
$path_f = implode( '/', array_slice( $path_split, 3 ) ); $path_f = implode('/', array_slice($path_split, 3));
// if path is empty we cannot resolve anything // if path is empty we cannot resolve anything
if ( empty( $path_f ) ) { if (empty($path_f)) {
return $size; return $size;
} }
$fileInfo = false; $fileInfo = false;
// get file info from database/cache if not .part file // get file info from database/cache if not .part file
if ( !Keymanager::isPartialFilePath( $path ) ) { if (!Keymanager::isPartialFilePath($path)) {
$fileInfo = $view->getFileInfo( $path ); $fileInfo = $view->getFileInfo($path);
} }
// if file is encrypted return real file size // if file is encrypted return real file size
if ( is_array( $fileInfo ) && $fileInfo['encrypted'] === true ) { if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
$size = $fileInfo['unencrypted_size']; $size = $fileInfo['unencrypted_size'];
} else { } else {
// self healing if file was removed from file cache // self healing if file was removed from file cache
if ( !is_array( $fileInfo ) ) { if (!is_array($fileInfo)) {
$fileInfo = array(); $fileInfo = array();
} }
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$fixSize = $util->getFileSize( $path ); $fixSize = $util->getFileSize($path);
if ( $fixSize > 0 ) { if ($fixSize > 0) {
$size = $fixSize; $size = $fixSize;
$fileInfo['encrypted'] = true; $fileInfo['encrypted'] = true;
$fileInfo['unencrypted_size'] = $size; $fileInfo['unencrypted_size'] = $size;
// put file info if not .part file // put file info if not .part file
if ( !Keymanager::isPartialFilePath( $path_f ) ) { if (!Keymanager::isPartialFilePath($path_f)) {
$view->putFileInfo( $path, $fileInfo ); $view->putFileInfo($path, $fileInfo);
} }
} }
@ -432,32 +436,32 @@ class Proxy extends \OC_FileProxy
/** /**
* @param $path * @param $path
*/ */
public function handleFile( $path ) { public function handleFile($path) {
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$session = new Session( $view ); $session = new Session($view);
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode( '/', $path ); $path_split = explode('/', $path);
$path_f = implode( '/', array_slice( $path_split, 3 ) ); $path_f = implode('/', array_slice($path_split, 3));
// only if file is on 'files' folder fix file size and sharing // only if file is on 'files' folder fix file size and sharing
if ( count($path_split) >= 2 && $path_split[2] == 'files' && $util->fixFileSize( $path ) ) { if (count($path_split) >= 2 && $path_split[2] == 'files' && $util->fixFileSize($path)) {
// get sharing app state // get sharing app state
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
// get users // get users
$usersSharing = $util->getSharingUsersArray( $sharingEnabled, $path_f ); $usersSharing = $util->getSharingUsersArray($sharingEnabled, $path_f);
// update sharing-keys // update sharing-keys
$util->setSharedFileKeyfiles( $session, $usersSharing, $path_f ); $util->setSharedFileKeyfiles($session, $usersSharing, $path_f);
} }
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;

View File

@ -26,8 +26,7 @@ namespace OCA\Encryption;
* Class for handling encryption related session data * Class for handling encryption related session data
*/ */
class Session class Session {
{
private $view; private $view;
@ -37,26 +36,26 @@ class Session
* *
* @note The ownCloud key pair is used to allow public link sharing even if encryption is enabled * @note The ownCloud key pair is used to allow public link sharing even if encryption is enabled
*/ */
public function __construct( $view ) { public function __construct($view) {
$this->view = $view; $this->view = $view;
if ( !$this->view->is_dir( 'owncloud_private_key' ) ) { if (!$this->view->is_dir('owncloud_private_key')) {
$this->view->mkdir( 'owncloud_private_key' ); $this->view->mkdir('owncloud_private_key');
} }
$publicShareKeyId = \OC_Appconfig::getValue( 'files_encryption', 'publicShareKeyId' ); $publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
if ( $publicShareKeyId === null ) { if ($publicShareKeyId === null) {
$publicShareKeyId = 'pubShare_' . substr( md5( time() ), 0, 8 ); $publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
\OC_Appconfig::setValue( 'files_encryption', 'publicShareKeyId', $publicShareKeyId ); \OC_Appconfig::setValue('files_encryption', 'publicShareKeyId', $publicShareKeyId);
} }
if ( if (
!$this->view->file_exists( "/public-keys/" . $publicShareKeyId . ".public.key" ) !$this->view->file_exists("/public-keys/" . $publicShareKeyId . ".public.key")
|| !$this->view->file_exists( "/owncloud_private_key/" . $publicShareKeyId . ".private.key" ) || !$this->view->file_exists("/owncloud_private_key/" . $publicShareKeyId . ".private.key")
) { ) {
$keypair = Crypt::createKeypair(); $keypair = Crypt::createKeypair();
@ -67,33 +66,35 @@ class Session
// Save public key // Save public key
if ( !$view->is_dir( '/public-keys' ) ) { if (!$view->is_dir('/public-keys')) {
$view->mkdir( '/public-keys' ); $view->mkdir('/public-keys');
} }
$this->view->file_put_contents( '/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey'] ); $this->view->file_put_contents('/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey']);
// Encrypt private key empty passphrase // Encrypt private key empty passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' ); $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], '');
// Save private key // Save private key
$this->view->file_put_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key', $encryptedPrivateKey ); $this->view->file_put_contents(
'/owncloud_private_key/' . $publicShareKeyId . '.private.key', $encryptedPrivateKey);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
} }
if ( \OCP\USER::getUser() === false || if (\OCP\USER::getUser() === false
( isset( $_GET['service'] ) && $_GET['service'] == 'files' && || (isset($_GET['service']) && $_GET['service'] == 'files'
isset( $_GET['t'] ) ) && isset($_GET['t']))
) { ) {
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key' ); $encryptedKey = $this->view->file_get_contents(
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' ); '/owncloud_private_key/' . $publicShareKeyId . '.private.key');
$this->setPrivateKey( $privateKey ); $privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, '');
$this->setPrivateKey($privateKey);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
} }
@ -104,7 +105,7 @@ class Session
* @param string $privateKey * @param string $privateKey
* @return bool * @return bool
*/ */
public function setPrivateKey( $privateKey ) { public function setPrivateKey($privateKey) {
$_SESSION['privateKey'] = $privateKey; $_SESSION['privateKey'] = $privateKey;
@ -120,8 +121,8 @@ class Session
public function getPrivateKey() { public function getPrivateKey() {
if ( if (
isset( $_SESSION['privateKey'] ) isset($_SESSION['privateKey'])
&& !empty( $_SESSION['privateKey'] ) && !empty($_SESSION['privateKey'])
) { ) {
return $_SESSION['privateKey']; return $_SESSION['privateKey'];
@ -139,7 +140,7 @@ class Session
* @param $legacyKey * @param $legacyKey
* @return bool * @return bool
*/ */
public function setLegacyKey( $legacyKey ) { public function setLegacyKey($legacyKey) {
$_SESSION['legacyKey'] = $legacyKey; $_SESSION['legacyKey'] = $legacyKey;
@ -154,8 +155,8 @@ class Session
public function getLegacyKey() { public function getLegacyKey() {
if ( if (
isset( $_SESSION['legacyKey'] ) isset($_SESSION['legacyKey'])
&& !empty( $_SESSION['legacyKey'] ) && !empty($_SESSION['legacyKey'])
) { ) {
return $_SESSION['legacyKey']; return $_SESSION['legacyKey'];

View File

@ -48,8 +48,7 @@ namespace OCA\Encryption;
* previous version deleted, this is handled by OC\Files\View, and thus the * previous version deleted, this is handled by OC\Files\View, and thus the
* encryption proxies are used and keyfiles deleted. * encryption proxies are used and keyfiles deleted.
*/ */
class Stream class Stream {
{
private $plainKey; private $plainKey;
private $encKeyfiles; private $encKeyfiles;
@ -77,18 +76,18 @@ class Stream
* @param $opened_path * @param $opened_path
* @return bool * @return bool
*/ */
public function stream_open( $path, $mode, $options, &$opened_path ) { public function stream_open($path, $mode, $options, &$opened_path) {
if ( !isset( $this->rootView ) ) { if (!isset($this->rootView)) {
$this->rootView = new \OC_FilesystemView( '/' ); $this->rootView = new \OC_FilesystemView('/');
} }
$util = new Util( $this->rootView, \OCP\USER::getUser() ); $util = new Util($this->rootView, \OCP\USER::getUser());
$this->userId = $util->getUserId(); $this->userId = $util->getUserId();
// Strip identifier text from path, this gives us the path relative to data/<user>/files // Strip identifier text from path, this gives us the path relative to data/<user>/files
$this->relPath = \OC\Files\Filesystem::normalizePath( str_replace( 'crypt://', '', $path ) ); $this->relPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path));
// rawPath is relative to the data directory // rawPath is relative to the data directory
$this->rawPath = $util->getUserFilesDir() . $this->relPath; $this->rawPath = $util->getUserFilesDir() . $this->relPath;
@ -110,25 +109,25 @@ class Stream
} else { } else {
$this->size = $this->rootView->filesize( $this->rawPath, $mode ); $this->size = $this->rootView->filesize($this->rawPath, $mode);
} }
$this->handle = $this->rootView->fopen( $this->rawPath, $mode ); $this->handle = $this->rootView->fopen($this->rawPath, $mode);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( !is_resource( $this->handle ) ) { if (!is_resource($this->handle)) {
\OCP\Util::writeLog( 'files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR ); \OCP\Util::writeLog('files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR);
} else { } else {
$this->meta = stream_get_meta_data( $this->handle ); $this->meta = stream_get_meta_data($this->handle);
} }
return is_resource( $this->handle ); return is_resource($this->handle);
} }
@ -136,11 +135,11 @@ class Stream
* @param $offset * @param $offset
* @param int $whence * @param int $whence
*/ */
public function stream_seek( $offset, $whence = SEEK_SET ) { public function stream_seek($offset, $whence = SEEK_SET) {
$this->flush(); $this->flush();
fseek( $this->handle, $offset, $whence ); fseek($this->handle, $offset, $whence);
} }
@ -149,36 +148,37 @@ class Stream
* @return bool|string * @return bool|string
* @throws \Exception * @throws \Exception
*/ */
public function stream_read( $count ) { public function stream_read($count) {
$this->writeCache = ''; $this->writeCache = '';
if ( $count != 8192 ) { if ($count != 8192) {
// $count will always be 8192 https://bugs.php.net/bug.php?id=21641 // $count will always be 8192 https://bugs.php.net/bug.php?id=21641
// This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed' // This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed'
\OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL ); \OCP\Util::writeLog('files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL);
die(); die();
} }
// Get the data from the file handle // Get the data from the file handle
$data = fread( $this->handle, 8192 ); $data = fread($this->handle, 8192);
$result = ''; $result = '';
if ( strlen( $data ) ) { if (strlen($data)) {
if ( !$this->getKey() ) { if (!$this->getKey()) {
// Error! We don't have a key to decrypt the file with // Error! We don't have a key to decrypt the file with
throw new \Exception( 'Encryption key not found for "' . $this->rawPath . '" during attempted read via stream' ); throw new \Exception(
'Encryption key not found for "' . $this->rawPath . '" during attempted read via stream');
} }
// Decrypt data // Decrypt data
$result = Crypt::symmetricDecryptFileContent( $data, $this->plainKey ); $result = Crypt::symmetricDecryptFileContent($data, $this->plainKey);
} }
@ -192,10 +192,10 @@ class Stream
* @param string $key key to use for encryption * @param string $key key to use for encryption
* @return string encrypted data on success, false on failure * @return string encrypted data on success, false on failure
*/ */
public function preWriteEncrypt( $plainData, $key ) { public function preWriteEncrypt($plainData, $key) {
// Encrypt data to 'catfile', which includes IV // Encrypt data to 'catfile', which includes IV
if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) { if ($encrypted = Crypt::symmetricEncryptFileContent($plainData, $key)) {
return $encrypted; return $encrypted;
@ -215,7 +215,7 @@ class Stream
public function getKey() { public function getKey() {
// Check if key is already set // Check if key is already set
if ( isset( $this->plainKey ) && isset( $this->encKeyfile ) ) { if (isset($this->plainKey) && isset($this->encKeyfile)) {
return true; return true;
@ -223,18 +223,18 @@ class Stream
// Fetch and decrypt keyfile // Fetch and decrypt keyfile
// Fetch existing keyfile // Fetch existing keyfile
$this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->relPath ); $this->encKeyfile = Keymanager::getFileKey($this->rootView, $this->userId, $this->relPath);
// If a keyfile already exists // If a keyfile already exists
if ( $this->encKeyfile ) { if ($this->encKeyfile) {
$session = new Session( $this->rootView ); $session = new Session($this->rootView);
$privateKey = $session->getPrivateKey( $this->userId ); $privateKey = $session->getPrivateKey($this->userId);
$shareKey = Keymanager::getShareKey( $this->rootView, $this->userId, $this->relPath ); $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $this->relPath);
$this->plainKey = Crypt::multiKeyDecrypt( $this->encKeyfile, $shareKey, $privateKey ); $this->plainKey = Crypt::multiKeyDecrypt($this->encKeyfile, $shareKey, $privateKey);
return true; return true;
@ -255,7 +255,7 @@ class Stream
* @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read * @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read
* @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek * @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek
*/ */
public function stream_write( $data ) { public function stream_write($data) {
// Disable the file proxies so that encryption is not // Disable the file proxies so that encryption is not
// automatically attempted when the file is written to disk - // automatically attempted when the file is written to disk -
@ -265,16 +265,16 @@ class Stream
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Get the length of the unencrypted data that we are handling // Get the length of the unencrypted data that we are handling
$length = strlen( $data ); $length = strlen($data);
// Find out where we are up to in the writing of data to the // Find out where we are up to in the writing of data to the
// file // file
$pointer = ftell( $this->handle ); $pointer = ftell($this->handle);
// Get / generate the keyfile for the file we're handling // Get / generate the keyfile for the file we're handling
// If we're writing a new file (not overwriting an existing // If we're writing a new file (not overwriting an existing
// one), save the newly generated keyfile // one), save the newly generated keyfile
if ( !$this->getKey() ) { if (!$this->getKey()) {
$this->plainKey = Crypt::generateKey(); $this->plainKey = Crypt::generateKey();
@ -282,7 +282,7 @@ class Stream
// If extra data is left over from the last round, make sure it // If extra data is left over from the last round, make sure it
// is integrated into the next 6126 / 8192 block // is integrated into the next 6126 / 8192 block
if ( $this->writeCache ) { if ($this->writeCache) {
// Concat writeCache to start of $data // Concat writeCache to start of $data
$data = $this->writeCache . $data; $data = $this->writeCache . $data;
@ -294,15 +294,15 @@ class Stream
} }
// While there still remains some data to be processed & written // While there still remains some data to be processed & written
while ( strlen( $data ) > 0 ) { while (strlen($data) > 0) {
// Remaining length for this iteration, not of the // Remaining length for this iteration, not of the
// entire file (may be greater than 8192 bytes) // entire file (may be greater than 8192 bytes)
$remainingLength = strlen( $data ); $remainingLength = strlen($data);
// If data remaining to be written is less than the // If data remaining to be written is less than the
// size of 1 6126 byte block // size of 1 6126 byte block
if ( $remainingLength < 6126 ) { if ($remainingLength < 6126) {
// Set writeCache to contents of $data // Set writeCache to contents of $data
// The writeCache will be carried over to the // The writeCache will be carried over to the
@ -320,25 +320,25 @@ class Stream
} else { } else {
// Read the chunk from the start of $data // Read the chunk from the start of $data
$chunk = substr( $data, 0, 6126 ); $chunk = substr($data, 0, 6126);
$encrypted = $this->preWriteEncrypt( $chunk, $this->plainKey ); $encrypted = $this->preWriteEncrypt($chunk, $this->plainKey);
// Write the data chunk to disk. This will be // Write the data chunk to disk. This will be
// attended to the last data chunk if the file // attended to the last data chunk if the file
// being handled totals more than 6126 bytes // being handled totals more than 6126 bytes
fwrite( $this->handle, $encrypted ); fwrite($this->handle, $encrypted);
// Remove the chunk we just processed from // Remove the chunk we just processed from
// $data, leaving only unprocessed data in $data // $data, leaving only unprocessed data in $data
// var, for handling on the next round // var, for handling on the next round
$data = substr( $data, 6126 ); $data = substr($data, 6126);
} }
} }
$this->size = max( $this->size, $pointer + $length ); $this->size = max($this->size, $pointer + $length);
$this->unencryptedSize += $length; $this->unencryptedSize += $length;
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -353,17 +353,17 @@ class Stream
* @param $arg1 * @param $arg1
* @param $arg2 * @param $arg2
*/ */
public function stream_set_option( $option, $arg1, $arg2 ) { public function stream_set_option($option, $arg1, $arg2) {
$return = false; $return = false;
switch ( $option ) { switch ($option) {
case STREAM_OPTION_BLOCKING: case STREAM_OPTION_BLOCKING:
$return = stream_set_blocking( $this->handle, $arg1 ); $return = stream_set_blocking($this->handle, $arg1);
break; break;
case STREAM_OPTION_READ_TIMEOUT: case STREAM_OPTION_READ_TIMEOUT:
$return = stream_set_timeout( $this->handle, $arg1, $arg2 ); $return = stream_set_timeout($this->handle, $arg1, $arg2);
break; break;
case STREAM_OPTION_WRITE_BUFFER: case STREAM_OPTION_WRITE_BUFFER:
$return = stream_set_write_buffer( $this->handle, $arg1 ); $return = stream_set_write_buffer($this->handle, $arg1);
} }
return $return; return $return;
@ -373,14 +373,14 @@ class Stream
* @return array * @return array
*/ */
public function stream_stat() { public function stream_stat() {
return fstat( $this->handle ); return fstat($this->handle);
} }
/** /**
* @param $mode * @param $mode
*/ */
public function stream_lock( $mode ) { public function stream_lock($mode) {
return flock( $this->handle, $mode ); return flock($this->handle, $mode);
} }
/** /**
@ -388,7 +388,7 @@ class Stream
*/ */
public function stream_flush() { public function stream_flush() {
return fflush( $this->handle ); return fflush($this->handle);
// Not a typo: http://php.net/manual/en/function.fflush.php // Not a typo: http://php.net/manual/en/function.fflush.php
} }
@ -397,19 +397,19 @@ class Stream
* @return bool * @return bool
*/ */
public function stream_eof() { public function stream_eof() {
return feof( $this->handle ); return feof($this->handle);
} }
private function flush() { private function flush() {
if ( $this->writeCache ) { if ($this->writeCache) {
// Set keyfile property for file in question // Set keyfile property for file in question
$this->getKey(); $this->getKey();
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->plainKey ); $encrypted = $this->preWriteEncrypt($this->writeCache, $this->plainKey);
fwrite( $this->handle, $encrypted ); fwrite($this->handle, $encrypted);
$this->writeCache = ''; $this->writeCache = '';
@ -434,33 +434,33 @@ class Stream
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Fetch user's public key // Fetch user's public key
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId ); $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->userId);
// Check if OC sharing api is enabled // Check if OC sharing api is enabled
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
$util = new Util( $this->rootView, $this->userId ); $util = new Util($this->rootView, $this->userId);
// Get all users sharing the file includes current user // Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId ); $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId);
// Fetch public keys for all sharing users // Fetch public keys for all sharing users
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds ); $publicKeys = Keymanager::getPublicKeys($this->rootView, $uniqueUserIds);
// Encrypt enc key for all sharing users // Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys ); $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys);
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
// Save the new encrypted file key // Save the new encrypted file key
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] ); Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']);
// Save the sharekeys // Save the sharekeys
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] ); Keymanager::setShareKeys($view, $this->relPath, $this->encKeyfiles['keys']);
// get file info // get file info
$fileInfo = $view->getFileInfo( $this->rawPath ); $fileInfo = $view->getFileInfo($this->rawPath);
if ( !is_array( $fileInfo ) ) { if (!is_array($fileInfo)) {
$fileInfo = array(); $fileInfo = array();
} }
@ -473,10 +473,10 @@ class Stream
$fileInfo['unencrypted_size'] = $this->unencryptedSize; $fileInfo['unencrypted_size'] = $this->unencryptedSize;
// set fileinfo // set fileinfo
$view->putFileInfo( $this->rawPath, $fileInfo ); $view->putFileInfo($this->rawPath, $fileInfo);
} }
return fclose( $this->handle ); return fclose($this->handle);
} }