protected function setUp() { parent::setUp(); // login user self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1); \OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1); $this->userId = self::TEST_ENCRYPTION_UTIL_USER1; $this->pass = self::TEST_ENCRYPTION_UTIL_USER1; // set content for encrypting / decrypting in tests $this->dataUrl = __DIR__ . '/../lib/crypt.php'; $this->dataShort = 'hats'; $this->dataLong = file_get_contents(__DIR__ . '/../lib/crypt.php'); $this->legacyData = __DIR__ . '/legacy-text.txt'; $this->legacyEncryptedData = __DIR__ . '/legacy-encrypted-text.txt'; $this->legacyEncryptedDataKey = __DIR__ . '/encryption.key'; $this->legacyKey = "30943623843030686906"; $keypair = \OCA\Files_Encryption\Crypt::createKeypair(); $this->genPublicKey = $keypair['publicKey']; $this->genPrivateKey = $keypair['privateKey']; $this->publicKeyDir = \OCA\Files_Encryption\Keymanager::getPublicKeyPath(); $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; $this->keysPath = $this->encryptionDir . '/' . 'keys'; $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.publicKey'; // e.g. data/public-keys/admin.publicKey $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.privateKey'; // e.g. data/admin/admin.privateKey $this->view = new \OC\Files\View('/'); $this->util = new \OCA\Files_Encryption\Util($this->view, $this->userId); // remember files_trashbin state $this->stateFilesTrashbin = \OC_App::isEnabled('files_trashbin'); // we don't want to tests with app files_trashbin enabled \OC_App::disable('files_trashbin'); }
/** * decrypt private key and add it to the current session * @param array $params with 'uid' and 'password' * @return mixed session or false */ public function initEncryption($params) { $session = new Session($this->view); // we tried to initialize the encryption app for this session $session->setInitialized(Session::INIT_EXECUTED); $encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']); $privateKey = false; if ($encryptedKey) { $privateKey = Crypt::decryptPrivateKey($encryptedKey, $params['password']); } if ($privateKey === false) { \OCP\Util::writeLog('Encryption library', 'Private key for user "' . $params['uid'] . '" is not valid! Maybe the user password was changed from outside if so please change it back to gain access', \OCP\Util::ERROR); return false; } $session->setPrivateKey($privateKey); $session->setInitialized(Session::INIT_SUCCESSFUL); return $session; }
/** * @brief replacing encryption keys during password change should be allowed * until the user logged in for the first time */ public function testSetPassphrase() { $view = new \OC\Files\View(); // set user password for the first time \OC_User::createUser(self::TEST_ENCRYPTION_HOOKS_USER3, 'newUserPassword'); \OCA\Files_Encryption\Hooks::postCreateUser(array('uid' => self::TEST_ENCRYPTION_HOOKS_USER3, 'password' => 'newUserPassword')); $this->assertTrue($view->file_exists(\OCA\Files_Encryption\Keymanager::getPublicKeyPath() . '/' . self::TEST_ENCRYPTION_HOOKS_USER3 . '.publicKey')); $this->assertTrue($view->file_exists(self::TEST_ENCRYPTION_HOOKS_USER3 . '/files_encryption/' . self::TEST_ENCRYPTION_HOOKS_USER3 . '.privateKey')); // check if we are able to decrypt the private key $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3); $privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'newUserPassword'); $this->assertTrue(is_string($privateKey)); // change the password before the user logged-in for the first time, // we can replace the encryption keys \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => self::TEST_ENCRYPTION_HOOKS_USER3, 'password' => 'passwordChanged')); $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3); $privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); $this->assertTrue(is_string($privateKey)); // now create a files folder to simulate a already used account $view->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER3 . '/files'); // change the password after the user logged in, now the password should not change \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => self::TEST_ENCRYPTION_HOOKS_USER3, 'password' => 'passwordChanged2')); $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3); $privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged2'); $this->assertFalse($privateKey); $privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); $this->assertTrue(is_string($privateKey)); }
$errorMessage = $l->t('Please repeat the new recovery password'); \OCP\JSON::error(array('data' => array('message' => $errorMessage))); exit; } if ($_POST['newPassword'] !== $_POST['confirmPassword']) { $errorMessage = $l->t('Repeated recovery key password does not match the provided recovery key password'); \OCP\JSON::error(array('data' => array('message' => $errorMessage))); exit; } $view = new \OC\Files\View('/'); $util = new \OCA\Files_Encryption\Util(new \OC\Files\View('/'), \OCP\User::getUser()); $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $keyId = $util->getRecoveryKeyId(); $encryptedRecoveryKey = \OCA\Files_Encryption\Keymanager::getPrivateSystemKey($keyId); $decryptedRecoveryKey = $encryptedRecoveryKey ? \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedRecoveryKey, $oldPassword) : false; if ($decryptedRecoveryKey) { $cipher = \OCA\Files_Encryption\Helper::getCipher(); $encryptedKey = \OCA\Files_Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword, $cipher); if ($encryptedKey) { \OCA\Files_Encryption\Keymanager::setPrivateSystemKey($encryptedKey, $keyId); $return = true; } } \OC_FileProxy::$enabled = $proxyStatus; // success or failure if ($return) { \OCP\JSON::success(array('data' => array('message' => $l->t('Password successfully changed.')))); } else { \OCP\JSON::error(array('data' => array('message' => $l->t('Could not change the password. Maybe the old password was not correct.')))); }
/** * @return bool */ public function stream_close() { $this->flush(); // if there is no valid private key return false if ($this->privateKey === false) { // cleanup if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && !$this->isLocalTmpFile) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; if ($this->rootView->file_exists($this->rawPath) && $this->size === 0) { fclose($this->handle); $this->rootView->unlink($this->rawPath); } // Re-enable proxy - our work is done \OC_FileProxy::$enabled = $proxyStatus; } // if private key is not valid redirect user to a error page Helper::redirectToErrorPage($this->session); } if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && $this->isLocalTmpFile === false && $this->size > 0 && $this->unencryptedSize > 0) { // only write keyfiles if it was a new file if ($this->newFile === true) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; // Fetch user's public key $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->keyId); // Check if OC sharing api is enabled $sharingEnabled = \OCP\Share::isEnabled(); // Get all users sharing the file includes current user $uniqueUserIds = $this->util->getSharingUsersArray($sharingEnabled, $this->relPath); $checkedUserIds = $this->util->filterShareReadyUsers($uniqueUserIds); // Fetch public keys for all sharing users $publicKeys = Keymanager::getPublicKeys($this->rootView, $checkedUserIds['ready']); // Encrypt enc key for all sharing users $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys); // Save the new encrypted file key Keymanager::setFileKey($this->rootView, $this->util, $this->relPath, $this->encKeyfiles['data']); // Save the sharekeys Keymanager::setShareKeys($this->rootView, $this->util, $this->relPath, $this->encKeyfiles['keys']); // Re-enable proxy - our work is done \OC_FileProxy::$enabled = $proxyStatus; } // we need to update the file info for the real file, not for the // part file. $path = Helper::stripPartialFileExtension($this->rawPath); $fileInfo = array('mimetype' => $this->rootView->getMimeType($this->rawPath), 'encrypted' => true, 'unencrypted_size' => $this->unencryptedSize); // if we write a part file we also store the unencrypted size for // the part file so that it can be re-used later $this->rootView->putFileInfo($this->rawPath, $fileInfo); if ($path !== $this->rawPath) { $this->rootView->putFileInfo($path, $fileInfo); } } $result = fclose($this->handle); if ($result === false) { \OCP\Util::writeLog('Encryption library', 'Could not close stream, file could be corrupted', \OCP\Util::FATAL); } return $result; }
/** * @large */ public function testMultiKeyEncrypt() { # TODO: search in keyfile for actual content as IV will ensure this test always passes $pair1 = \OCA\Files_Encryption\Crypt::createKeypair(); $this->assertEquals(2, count($pair1)); $this->assertTrue(strlen($pair1['publicKey']) > 1); $this->assertTrue(strlen($pair1['privateKey']) > 1); $crypted = \OCA\Files_Encryption\Crypt::multiKeyEncrypt($this->dataShort, array($pair1['publicKey'])); $this->assertNotEquals($this->dataShort, $crypted['data']); $decrypt = \OCA\Files_Encryption\Crypt::multiKeyDecrypt($crypted['data'], $crypted['keys'][0], $pair1['privateKey']); $this->assertEquals($this->dataShort, $decrypt); }
/** * test webdav put random file */ function testWebdavPUT() { // generate filename $filename = '/tmp-' . $this->getUniqueID() . '.txt'; // set server vars $_SERVER['REQUEST_METHOD'] = 'OPTIONS'; $_SERVER['REQUEST_METHOD'] = 'PUT'; $_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename; $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dGVzdC13ZWJkYXYtdXNlcjE6dGVzdC13ZWJkYXYtdXNlcjE='; $_SERVER['CONTENT_TYPE'] = 'application/octet-stream'; $_SERVER['PATH_INFO'] = '/webdav' . $filename; $_SERVER['CONTENT_LENGTH'] = strlen($this->dataShort); // handle webdav request $this->handleWebdavRequest($this->dataShort); // check if file was created $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files' . $filename)); // check if key-file was created $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/keys/' . $filename . '/fileKey')); // check if shareKey-file was created $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/keys/' . $filename . '/' . $this->userId . '.shareKey')); // disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; // get encrypted file content $encryptedContent = $this->view->file_get_contents('/' . $this->userId . '/files' . $filename); // restore proxy state \OC_FileProxy::$enabled = $proxyStatus; // check if encrypted content is valid $this->assertTrue(\OCA\Files_Encryption\Crypt::isCatfileContent($encryptedContent)); // get decrypted file contents $decrypt = file_get_contents('crypt:///' . $this->userId . '/files' . $filename); // check if file content match with the written content $this->assertEquals($this->dataShort, $decrypt); // return filename for next test return $filename; }
/** * @medium */ function testGetUserKeys() { $keys = \OCA\Files_Encryption\Keymanager::getUserKeys($this->view, $this->userId); $resPublic = openssl_pkey_get_public($keys['publicKey']); $this->assertTrue(is_resource($resPublic)); $sslInfoPublic = openssl_pkey_get_details($resPublic); $this->assertArrayHasKey('key', $sslInfoPublic); $privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($keys['privateKey'], $this->pass); $resPrivate = openssl_pkey_get_private($privateKey); $this->assertTrue(is_resource($resPrivate)); $sslInfoPrivate = openssl_pkey_get_details($resPrivate); $this->assertArrayHasKey('key', $sslInfoPrivate); }