From b2e03895119c6ceaca839f617eb3c60ae3cd92ea Mon Sep 17 00:00:00 2001 From: Sujith H Date: Wed, 28 Jun 2017 14:56:23 +0530 Subject: [PATCH] Change to re-create masterkeys This change brings a new command to re-create masterkey Signed-off-by: Sujith H --- apps/encryption/appinfo/info.xml | 1 + .../lib/Command/RecreateMasterKey.php | 320 ++++++++++++++++++ apps/encryption/lib/KeyManager.php | 36 +- .../tests/Command/RecreateMasterKeyTest.php | 302 +++++++++++++++++ apps/encryption/tests/KeyManagerTest.php | 58 +++- .../Files/Storage/Wrapper/Encryption.php | 32 +- 6 files changed, 724 insertions(+), 25 deletions(-) create mode 100644 apps/encryption/lib/Command/RecreateMasterKey.php create mode 100644 apps/encryption/tests/Command/RecreateMasterKeyTest.php diff --git a/apps/encryption/appinfo/info.xml b/apps/encryption/appinfo/info.xml index 3c0e52c792e4..2a7ae569868d 100644 --- a/apps/encryption/appinfo/info.xml +++ b/apps/encryption/appinfo/info.xml @@ -29,6 +29,7 @@ OCA\Encryption\Command\SelectEncryptionType + OCA\Encryption\Command\RecreateMasterKey OCA\Encryption\Command\MigrateKeys diff --git a/apps/encryption/lib/Command/RecreateMasterKey.php b/apps/encryption/lib/Command/RecreateMasterKey.php new file mode 100644 index 000000000000..3ab23ac2c53e --- /dev/null +++ b/apps/encryption/lib/Command/RecreateMasterKey.php @@ -0,0 +1,320 @@ + +* +* @copyright Copyright (c) 2017, ownCloud GmbH +* @license AGPL-3.0 +* +* This code is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License, version 3, +* along with this program. If not, see +* +*/ + + +namespace OCA\Encryption\Command; + +use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Encryption\Manager; +use OC\Files\View; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Util; +use OCP\App\IAppManager; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\ISession; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class RecreateMasterKey extends Command { + + /** @var Manager */ + protected $encryptionManager; + + /** @var IUserManager */ + protected $userManager; + + /** @var View */ + protected $rootView; + + /** @var KeyManager */ + protected $keyManager; + + /** @var Util */ + protected $util; + + /** @var IAppManager */ + protected $IAppManager; + + /** @var IAppConfig */ + protected $appConfig; + + /** @var IConfig */ + protected $IConfig; + + /** @var ISession */ + protected $ISession; + + /** @var array files which couldn't be decrypted */ + protected $failed; + + /** + * RecreateMasterKey constructor. + * + * @param Manager $encryptionManager + * @param IUserManager $userManager + * @param View $rootView + * @param KeyManager $keyManager + * @param Util $util + * @param IAppManager $IAppManager + * @param IAppConfig $appConfig + * @param IConfig $IConfig + * @param ISession $ISession + */ + public function __construct(Manager $encryptionManager, IUserManager $userManager, + View $rootView, KeyManager $keyManager, Util $util, + IAppManager $IAppManager, IAppConfig $appConfig, + IConfig $IConfig, ISession $ISession) { + + parent::__construct(); + $this->encryptionManager = $encryptionManager; + $this->userManager = $userManager; + $this->rootView = $rootView; + $this->keyManager = $keyManager; + $this->util = $util; + $this->IAppManager = $IAppManager; + $this->appConfig = $appConfig; + $this->IConfig = $IConfig; + $this->ISession = $ISession; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('encryption:recreate-master-key') + ->setDescription('Replace existing master key with new one. Encrypt the file system with newly created master key') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + if ($this->util->isMasterKeyEnabled()) { + $output->writeln("Decryption started\n"); + $progress = new ProgressBar($output); + $progress->start(); + $progress->setMessage("Decryption progress..."); + $progress->advance(); + $this->decryptAllUsersFiles($progress); + $progress->finish(); + + if (empty($this->failed)) { + + $this->IAppManager->disableApp('encryption'); + + //Delete the files_encryption dir + $this->rootView->deleteAll('files_encryption'); + + $this->appConfig->setValue('core', 'encryption_enabled', 'no'); + $this->appConfig->deleteKey('encryption', 'useMasterKey'); + $this->appConfig->deleteKey('encryption', 'masterKeyId'); + $this->appConfig->deleteKey('encryption', 'recoveryKeyId'); + $this->appConfig->deleteKey('encryption', 'publicShareKeyId'); + $this->appConfig->deleteKey('files_encryption', 'installed_version'); + + } + $output->writeln("\nDecryption completed\n"); + + //Reencrypt again + $this->IAppManager->enableApp('encryption'); + $this->appConfig->setValue('core', 'encryption_enabled', 'yes'); + $this->appConfig->setValue('encryption', 'enabled', 'yes'); + $output->writeln("Encryption started\n"); + + $output->writeln("Waiting for creating new masterkey\n"); + + $this->keyManager->setPublicShareKeyIDAndMasterKeyId(); + + $output->writeln("New masterkey created successfully\n"); + + $this->appConfig->setValue('encryption', 'enabled', 'yes'); + $this->appConfig->setValue('encryption', 'useMasterKey', '1'); + + $this->keyManager->validateShareKey(); + $this->keyManager->validateMasterKey(); + $progress->setMessage("Encryption progress..."); + $progress->start(); + $progress->advance(); + $this->encryptAllUsersFiles($progress); + $progress->finish(); + $output->writeln("\nEncryption completed successfully\n"); + } else { + $output->writeln("Master key is not enabled. Kindly enable it\n"); + } + } + + public function encryptAllUsersFiles(ProgressBar $progress) { + $userNo = 1; + foreach($this->userManager->getBackends() as $backend) { + $limit = 500; + $offset = 0; + do { + $users = $backend->getUsers('', $limit, $offset); + foreach ($users as $user) { + if($this->encryptionManager->isReadyForUser($user)) { + $this->encryptUsersFiles($user, $progress); + $progress->advance(); + } + $userNo++; + } + $offset += $limit; + } while(count($users) >= $limit); + } + } + + public function encryptUsersFiles($uid, ProgressBar $progress) { + + $this->setupUserFS($uid); + $directories = []; + $directories[] = '/' . $uid . '/files'; + + while($root = array_pop($directories)) { + $content = $this->rootView->getDirectoryContent($root); + foreach ($content as $file) { + $path = $root . '/' . $file['name']; + if ($this->rootView->is_dir($path)) { + $directories[] = $path; + continue; + } else { + if($this->encryptFile($path) === false) { + $progress->setMessage("encrypt files for user $uid: $path (already encrypted)"); + $progress->advance(); + } + } + } + } + } + + public function encryptFile($path) { + $source = $path; + $target = $path . '.encrypted.' . time(); + + try { + $this->ISession->set('encryptAllCmd', true); + $this->rootView->copy($source, $target); + $this->rootView->rename($target, $source); + $this->ISession->remove('encryptAllCmd'); + } catch (DecryptionFailedException $e) { + if ($this->rootView->file_exists($target)) { + $this->rootView->unlink($target); + } + return false; + } + + return true; + } + + protected function decryptAllUsersFiles(ProgressBar $progress) { + $userList = []; + + foreach ($this->userManager->getBackends() as $backend) { + $limit = 500; + $offset = 0; + do { + $users = $backend->getUsers('', $limit, $offset); + foreach ($users as $user) { + $userList[] = $user; + } + $offset += $limit; + } while (count($users) >= $limit); + } + + $userNo = 1; + foreach ($userList as $uid) { + $this->decryptUsersFiles($uid, $progress); + $progress->advance(); + $userNo++; + } + return true; + } + + protected function decryptUsersFiles($uid, ProgressBar $progress) { + $this->setupUserFS($uid); + $directories = []; + $directories[] = '/' . $uid . '/files'; + + while ($root = array_pop($directories)) { + $content = $this->rootView->getDirectoryContent($root); + foreach ($content as $file) { + // only decrypt files owned by the user + if($file->getStorage()->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) { + continue; + } + $path = $root . '/' . $file['name']; + if ($this->rootView->is_dir($path)) { + $directories[] = $path; + continue; + } else { + try { + if ($file->isEncrypted() !== false) { + if ($this->decryptFile($path) === false) { + $progress->setMessage("decrypt files for user $uid: $path (already decrypted)"); + $progress->advance(); + } + } + } catch (\Exception $e) { + if (isset($this->failed[$uid])) { + $this->failed[$uid][] = $path; + } else { + $this->failed[$uid] = [$path]; + } + } + } + } + } + + if (empty($this->failed)) { + $this->rootView->deleteAll("$uid/files_encryption"); + } + } + + protected function decryptFile($path) { + + $source = $path; + $target = $path . '.decrypted.' . $this->getTimestamp(); + + try { + $this->ISession->set('decryptAllCmd', true); + $this->rootView->copy($source, $target); + $this->rootView->rename($target, $source); + $this->keyManager->setVersion($source,0, $this->rootView); + $this->ISession->remove('decryptAllCmd'); + } catch (DecryptionFailedException $e) { + if ($this->rootView->file_exists($target)) { + $this->rootView->unlink($target); + } + return false; + } + + return true; + } + + protected function getTimestamp() { + return time(); + } + + protected function setupUserFS($uid) { + \OC_Util::tearDownFS(); + \OC_Util::setupFS($uid); + } +} diff --git a/apps/encryption/lib/KeyManager.php b/apps/encryption/lib/KeyManager.php index 210820bd0e28..797a43d72d20 100644 --- a/apps/encryption/lib/KeyManager.php +++ b/apps/encryption/lib/KeyManager.php @@ -132,19 +132,7 @@ public function __construct( $this->recoveryKeyId); } - $this->publicShareKeyId = $this->config->getAppValue('encryption', - 'publicShareKeyId'); - if (empty($this->publicShareKeyId)) { - $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); - $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); - } - - $this->masterKeyId = $this->config->getAppValue('encryption', - 'masterKeyId'); - if (empty($this->masterKeyId)) { - $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8); - $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId); - } + $this->setPublicShareKeyIDAndMasterKeyId(); $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; $this->log = $log; @@ -701,6 +689,9 @@ public function getMasterKeyPassword() { * @return string */ public function getMasterKeyId() { + if($this->config->getAppValue('encryption', 'masterKeyId') !== $this->masterKeyId) { + $this->masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId'); + } return $this->masterKeyId; } @@ -712,4 +703,23 @@ public function getMasterKeyId() { public function getPublicMasterKey() { return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID); } + + /** + * set publicShareKeyId and masterKeyId if not set + */ + public function setPublicShareKeyIDAndMasterKeyId() { + $this->publicShareKeyId = $this->config->getAppValue('encryption', + 'publicShareKeyId'); + if (empty($this->publicShareKeyId)) { + $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); + $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); + } + + $this->masterKeyId = $this->config->getAppValue('encryption', + 'masterKeyId'); + if (empty($this->masterKeyId)) { + $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8); + $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId); + } + } } diff --git a/apps/encryption/tests/Command/RecreateMasterKeyTest.php b/apps/encryption/tests/Command/RecreateMasterKeyTest.php new file mode 100644 index 000000000000..403d6f57ead2 --- /dev/null +++ b/apps/encryption/tests/Command/RecreateMasterKeyTest.php @@ -0,0 +1,302 @@ + + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + + +namespace OCA\Encryption\Tests\Command; + +use OC\Files\FileInfo; +use OC\Files\View; +use OCA\Encryption\Command\RecreateMasterKey; +use OC\Encryption\Exceptions\DecryptionFailedException; +use Test\TestCase; + +class RecreateMasterKeyTest extends TestCase { + + /** @var Manager | \PHPUnit_Framework_MockObject_MockObject */ + protected $encryptionManager; + + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + + /** @var View | \PHPUnit_Framework_MockObject_MockObject */ + protected $rootView; + + /** @var KeyManager | \PHPUnit_Framework_MockObject_MockObject */ + protected $keyManager; + + /** @var Util | \PHPUnit_Framework_MockObject_MockObject */ + protected $util; + + /** @var IAppManager | \PHPUnit_Framework_MockObject_MockObject */ + protected $IAppManager; + + /** @var IAppConfig | \PHPUnit_Framework_MockObject_MockObject */ + protected $appConfig; + + /** @var IConfig | \PHPUnit_Framework_MockObject_MockObject */ + protected $IConfig; + + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject */ + protected $ISession; + + /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\UserInterface */ + protected $userInterface; + + /** @var \Symfony\Component\Console\Output\OutputInterface | \PHPUnit_Framework_MockObject_MockObject */ + protected $output; + + /** @var \Symfony\Component\Console\Input\InputInterface | \PHPUnit_Framework_MockObject_MockObject */ + protected $input; + + protected $progressbar; + + /** @var RecreateMasterKey */ + protected $recreateMasterKey; + + public function setUp() { + parent::setUp(); + + $this->encryptionManager = $this->getMockBuilder('OC\Encryption\Manager') + ->disableOriginalConstructor()->getMock(); + $this->userManager = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor()->getMock(); + $this->rootView = $this->getMockBuilder('OC\Files\View') + ->disableOriginalConstructor()->getMock(); + $this->keyManager = $this->getMockBuilder('OCA\Encryption\KeyManager') + ->disableOriginalConstructor()->getMock(); + $this->util = $this->getMockBuilder('OCA\Encryption\Util') + ->disableOriginalConstructor()->getMock(); + $this->IAppManager = $this->getMockBuilder('OCP\App\IAppManager') + ->disableOriginalConstructor()->getMock(); + $this->appConfig = $this->getMockBuilder('OCP\IAppConfig') + ->disableOriginalConstructor()->getMock(); + $this->IConfig = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor()->getMock(); + $this->ISession = $this->getMockBuilder('OCP\ISession') + ->disableOriginalConstructor()->getMock(); + $this->input = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface') + ->disableOriginalConstructor()->getMock(); + $this->output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface') + ->disableOriginalConstructor()->getMock(); + $this->progressBar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar') + ->disableOriginalConstructor()->getMock(); + + $this->userInterface = $this->getMockBuilder('OCP\UserInterface') + ->disableOriginalConstructor()->getMock(); + + $this->output->expects($this->any())->method('getFormatter') + ->willReturn($this->createMock('\Symfony\Component\Console\Formatter\OutputFormatterInterface')); + + $this->recreateMasterKey = new RecreateMasterKey($this->encryptionManager, $this->userManager, + $this->rootView, $this->keyManager, $this->util, + $this->IAppManager, $this->appConfig, + $this->IConfig, $this->ISession); + + + $this->invokePrivate($this->recreateMasterKey, 'input', [$this->input]); + $this->invokePrivate($this->recreateMasterKey, 'output', [$this->output]); + } + + /** + * @dataProvider dataTestExecute + */ + public function testNewMasterKey($mastkerKeyEnabled) { + + if( $mastkerKeyEnabled === true) { + $this->recreateMasterKey = $this->getMockBuilder('OCA\Encryption\Command\RecreateMasterKey') + ->setConstructorArgs( + [ + $this->encryptionManager, $this->userManager, + $this->rootView, $this->keyManager, $this->util, + $this->IAppManager, $this->appConfig, + $this->IConfig, $this->ISession + ] + )->setMethods(['setupUserFS'])->getMock(); + + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn(true); + + $this->userManager->expects($this->any()) + ->method('getBackends') + ->willReturn([$this->userInterface]); + + $this->userInterface->expects($this->any()) + ->method('getUsers') + ->willReturn(['user1']); + + $storage = $this->getMockBuilder('OCA\Files_Sharing\SharedStorage') + ->disableOriginalConstructor()->getMock(); + + $this->rootView->expects($this->at(0))->method('getDirectoryContent') + ->with('/user1/files')->willReturn( + [ + new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type'=>'dir'], null), + new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type'=>'file', 'encrypted'=>true], null) + ] + ); + + $this->rootView->expects($this->at(5))->method('getDirectoryContent') + ->with('/user1/files/foo')->willReturn( + [ + new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type'=>'file', 'encrypted'=>true], null) + ] + ); + + $this->rootView->expects($this->any())->method('is_dir') + ->willReturnCallback( + function($path) { + if ($path === '/user1/files/foo') { + return true; + } + return false; + } + ); + + $this->encryptionManager->expects($this->any()) + ->method('isReadyForUser') + ->with('user1') + ->willReturn(true); + + $this->rootView->expects($this->at(11))->method('getDirectoryContent') + ->with('/user1/files')->willReturn( + [ + new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type'=>'dir'], null), + new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type'=>'file', 'encrypted'=>true], null) + ] + ); + + $this->rootView->expects($this->at(16))->method('getDirectoryContent') + ->with('/user1/files/foo')->willReturn( + [ + new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type'=>'file', 'encrypted'=>true], null) + ] + ); + + global $outputText; + $this->output->expects($this->at(34)) + ->method('writeln') + ->willReturnCallback(function ($value){ + global $outputText; + $outputText .= $value . "\n"; + }); + + $this->invokePrivate($this->recreateMasterKey, 'execute', [$this->input, $this->output]); + $this->assertSame("Encryption completed successfully", trim($outputText, "\n")); + $outputText=""; + } else { + $this->recreateMasterKey = $this->getMockBuilder('OCA\Encryption\Command\RecreateMasterKey') + ->setConstructorArgs( + [ + $this->encryptionManager, $this->userManager, + $this->rootView, $this->keyManager, $this->util, + $this->IAppManager, $this->appConfig, + $this->IConfig, $this->ISession + ] + )->setMethods(['setupUserFS'])->getMock(); + + $this->util->expects($this->once())->method('isMasterKeyEnabled') + ->willReturn($mastkerKeyEnabled); + + global $outputText; + $this->output->expects($this->at(0)) + ->method('writeln') + ->willReturnCallback(function ($value){ + global $outputText; + $outputText .= $value . "\n"; + }); + + $this->invokePrivate($this->recreateMasterKey, 'execute', [$this->input, $this->output]); + $this->assertSame("Master key is not enabled. Kindly enable it", trim($outputText, "\n")); + } + } + + public function dataTestExecute() { + return [ + [true], + [false] + ]; + } + + public function testDecryptFile() { + + $path = 'test.txt'; + + /** @var RecreateMasterKey | \PHPUnit_Framework_MockObject_MockObject $instance */ + $instance = $this->getMockBuilder('OCA\Encryption\Command\RecreateMasterKey') + ->setConstructorArgs( + [ + $this->encryptionManager, $this->userManager, + $this->rootView, $this->keyManager, $this->util, + $this->IAppManager, $this->appConfig, + $this->IConfig, $this->ISession + ] + )->setMethods(['getTimestamp'])->getMock(); + + $instance->expects($this->any())->method('getTimestamp')->willReturn(42); + + $this->rootView->expects($this->once()) + ->method('copy') + ->with($path, $path . '.decrypted.42'); + $this->rootView->expects($this->once()) + ->method('rename') + ->with($path . '.decrypted.42', $path); + + $this->assertTrue( + $this->invokePrivate($instance, 'decryptFile', [$path]) + ); + } + + public function testDecryptFileFailure() { + $path = 'test.txt'; + + /** @var RecreateMasterKey | \PHPUnit_Framework_MockObject_MockObject $instance */ + $instance = $this->getMockBuilder('OCA\Encryption\Command\RecreateMasterKey') + ->setConstructorArgs( + [ + $this->encryptionManager, $this->userManager, + $this->rootView, $this->keyManager, $this->util, + $this->IAppManager, $this->appConfig, + $this->IConfig, $this->ISession + ] + )->setMethods(['getTimestamp'])->getMock(); + + $instance->expects($this->any())->method('getTimestamp')->willReturn(42); + + $this->rootView->expects($this->once()) + ->method('copy') + ->with($path, $path . '.decrypted.42') + ->willReturnCallback(function() { throw new DecryptionFailedException();}); + + $this->rootView->expects($this->never())->method('rename'); + $this->rootView->expects($this->once()) + ->method('file_exists') + ->with($path . '.decrypted.42') + ->willReturn(true); + $this->rootView->expects($this->once()) + ->method('unlink') + ->with($path . '.decrypted.42'); + + $this->assertFalse( + $this->invokePrivate($instance, 'decryptFile', [$path]) + ); + } +} + diff --git a/apps/encryption/tests/KeyManagerTest.php b/apps/encryption/tests/KeyManagerTest.php index d812c0358038..852d117ab881 100644 --- a/apps/encryption/tests/KeyManagerTest.php +++ b/apps/encryption/tests/KeyManagerTest.php @@ -73,9 +73,12 @@ public function setUp() { ->disableOriginalConstructor() ->getMock(); $this->configMock = $this->createMock('OCP\IConfig'); + /*$this->configMock->expects($this->any()) + ->method('getAppValue') + ->willReturn($this->systemKeyId);*/ $this->configMock->expects($this->any()) ->method('getAppValue') - ->willReturn($this->systemKeyId); + ->will($this->returnCallback(array($this, 'returnAppValue'))); $this->userMock = $this->createMock('OCP\IUserSession'); $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session') ->disableOriginalConstructor() @@ -95,6 +98,15 @@ public function setUp() { $this->utilMock); } + public function returnAppValue() { + $args = func_get_args(); + if($args[1] === 'masterKeyId') { + return 'masterKeyId'; + } else { + return 'systemKeyId'; + } + } + public function testDeleteShareKey() { $this->keyStorageMock->expects($this->any()) ->method('deleteFileKey') @@ -499,16 +511,48 @@ public function dataTestAddSystemKeys() { } public function testGetMasterKeyId() { - $this->assertSame('systemKeyId', $this->instance->getMasterKeyId()); + $localConfigMock = $this->createMock('OCP\IConfig'); + $localConfigMock->expects($this->any()) + ->method('getAppValue') + ->willReturn($this->systemKeyId); + $instance = $this->getMockBuilder('OCA\Encryption\KeyManager') + ->setConstructorArgs( + [ + $this->keyStorageMock, + $this->cryptMock, + $localConfigMock, + $this->userMock, + $this->sessionMock, + $this->logMock, + $this->utilMock + ] + )->setMethods()->getMock(); + $this->assertSame('systemKeyId', $instance->getMasterKeyId()); } public function testGetPublicMasterKey() { + $localConfigMock = $this->createMock('OCP\IConfig'); + $localConfigMock->expects($this->any()) + ->method('getAppValue') + ->willReturn($this->systemKeyId); + $instance = $this->getMockBuilder('OCA\Encryption\KeyManager') + ->setConstructorArgs( + [ + $this->keyStorageMock, + $this->cryptMock, + $localConfigMock, + $this->userMock, + $this->sessionMock, + $this->logMock, + $this->utilMock + ] + )->setMethods()->getMock(); $this->keyStorageMock->expects($this->once())->method('getSystemUserKey') ->with('systemKeyId.publicKey', \OCA\Encryption\Crypto\Encryption::ID) ->willReturn(true); $this->assertTrue( - $this->instance->getPublicMasterKey() + $instance->getPublicMasterKey() ); } @@ -538,13 +582,19 @@ public function testGetMasterKeyPasswordException() { */ public function testValidateMasterKey($masterKey) { + $localConfigMock = $this->createMock('OCP\IConfig'); + $returnVal = ($masterKey) ? 'masterKeyId' : $this->systemKeyId; + $localConfigMock->expects($this->any()) + ->method('getAppValue') + ->willReturn($returnVal); + /** @var \OCA\Encryption\KeyManager | \PHPUnit_Framework_MockObject_MockObject $instance */ $instance = $this->getMockBuilder('OCA\Encryption\KeyManager') ->setConstructorArgs( [ $this->keyStorageMock, $this->cryptMock, - $this->configMock, + $localConfigMock, $this->userMock, $this->sessionMock, $this->logMock, diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index 0bbe9e67d345..849609f3a1f0 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -201,16 +201,18 @@ public function getMetaData($path) { */ public function file_get_contents($path) { - $encryptionModule = $this->getEncryptionModule($path); + if ( \OC::$server->getAppConfig()->getValue('encryption', 'enabled', false) !== false) { + $encryptionModule = $this->getEncryptionModule($path); - if ($encryptionModule) { - $handle = $this->fopen($path, "r"); - if (!$handle) { - return false; + if ($encryptionModule) { + $handle = $this->fopen($path, "r"); + if (!$handle) { + return false; + } + $data = stream_get_contents($handle); + fclose($handle); + return $data; } - $data = stream_get_contents($handle); - fclose($handle); - return $data; } return $this->storage->file_get_contents($path); } @@ -379,6 +381,8 @@ public function fopen($path, $mode) { $signed = (isset($header['signed']) && $header['signed'] === 'true') ? true : false; $fullPath = $this->getFullPath($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); + $createDecryptedFile = false; + $readNormalFile = false; if ($this->util->isExcluded($fullPath) === false) { @@ -420,6 +424,9 @@ public function fopen($path, $mode) { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); $shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath); $signed = true; + if (\OC::$server->getSession()->get('decryptAllCmd') === true) { + $createDecryptedFile = true; + } } } else { $info = $this->getCache()->get($path); @@ -436,6 +443,9 @@ public function fopen($path, $mode) { $shouldEncrypt = true; $targetIsEncrypted = true; } + if ( \OC::$server->getSession()->get('encryptAllCmd') === true) { + $readNormalFile = true; + } } } catch (ModuleDoesNotExistsException $e) { $this->logger->warning('Encryption module "' . $encryptionModuleId . @@ -450,6 +460,9 @@ public function fopen($path, $mode) { } if ($shouldEncrypt === true && $encryptionModule !== null) { + if (($createDecryptedFile === true) || ($readNormalFile === true)) { + return $this->storage->fopen($path, $mode); + } $headerSize = $this->getHeaderSize($path); $source = $this->storage->fopen($path, $mode); if (!is_resource($source)) { @@ -672,6 +685,9 @@ private function updateEncryptedVersion(Storage $sourceStorage, $sourceInternalP // in case of a rename we need to manipulate the source cache because // this information will be kept for the new target if ($isRename) { + $encryptedVersion = 1; + $cacheInformation['encryptedVersion'] = $encryptedVersion; + $sourceStorage->getCache()->put($sourceInternalPath, $cacheInformation); } else { $this->getCache()->put($targetInternalPath, $cacheInformation);