function setUp() { // login user \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); \OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); $this->userId = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1; $this->pass = \Test_Encryption_Util::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 = Encryption\Crypt::createKeypair(); $this->genPublicKey = $keypair['publicKey']; $this->genPrivateKey = $keypair['privateKey']; $this->publicKeyDir = '/' . 'public-keys'; $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key $this->view = new \OC_FilesystemView('/'); $this->util = new 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'); }
/** * if session is started, check if ownCloud key pair is set up, if not create it * @param \OC\Files\View $view * * @note The ownCloud key pair is used to allow public link sharing even if encryption is enabled */ public function __construct($view) { $this->view = $view; if (!$this->view->is_dir('owncloud_private_key')) { $this->view->mkdir('owncloud_private_key'); } $appConfig = \OC::$server->getAppConfig(); $publicShareKeyId = $appConfig->getValue('files_encryption', 'publicShareKeyId'); if ($publicShareKeyId === null) { $publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); $appConfig->setValue('files_encryption', 'publicShareKeyId', $publicShareKeyId); } if (!$this->view->file_exists("/public-keys/" . $publicShareKeyId . ".public.key") || !$this->view->file_exists("/owncloud_private_key/" . $publicShareKeyId . ".private.key")) { $keypair = Crypt::createKeypair(); // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; // Save public key if (!$view->is_dir('/public-keys')) { $view->mkdir('/public-keys'); } $this->view->file_put_contents('/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey']); // Encrypt private key empty passphrase $cipher = \OCA\Encryption\Helper::getCipher(); $encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], '', $cipher); if ($encryptedKey) { Keymanager::setPrivateSystemKey($encryptedKey, $publicShareKeyId . '.private.key'); } else { \OCP\Util::writeLog('files_encryption', 'Could not create public share keys', \OCP\Util::ERROR); } \OC_FileProxy::$enabled = $proxyStatus; } if (\OCA\Encryption\Helper::isPublicAccess()) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $encryptedKey = $this->view->file_get_contents('/owncloud_private_key/' . $publicShareKeyId . '.private.key'); $privateKey = Crypt::decryptPrivateKey($encryptedKey, ''); $this->setPublicSharePrivateKey($privateKey); $this->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL); \OC_FileProxy::$enabled = $proxyStatus; } }
/** * @medium * @brief test decryption using legacy blowfish method */ function testLegacyDecryptLong() { $crypted = $this->legacyEncrypt($this->dataLong, $this->pass); $decrypted = Encryption\Crypt::legacyBlockDecrypt($crypted, $this->pass); $this->assertEquals($this->dataLong, $decrypted); }
/** * @brief Change a user's encryption passphrase * @param array $params keys: uid, password */ public static function setPassphrase($params) { if (\OCP\App::isEnabled('files_encryption') === false) { return true; } // Only attempt to change passphrase if server-side encryption // is in use (client-side encryption does not have access to // the necessary keys) if (Crypt::mode() === 'server') { $view = new \OC_FilesystemView('/'); $session = new \OCA\Encryption\Session($view); // Get existing decrypted private key $privateKey = $session->getPrivateKey(); if ($params['uid'] === \OCP\User::getUser() && $privateKey) { // Encrypt private key with new user pwd as passphrase $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']); // Save private key if ($encryptedPrivateKey) { Keymanager::setPrivateKey($encryptedPrivateKey); } else { \OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR); } // NOTE: Session does not need to be updated as the // private key has not changed, only the passphrase // used to decrypt it has changed } else { // admin changed the password for a different user, create new keys and reencrypt file keys $user = $params['uid']; $util = new Util($view, $user); $recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null; // we generate new keys if... // ...we have a recovery password and the user enabled the recovery key // ...encryption was activated for the first time (no keys exists) // ...the user doesn't have any files if ($util->recoveryEnabledForUser() && $recoveryPassword || !$util->userKeysExists() || !$view->file_exists($user . '/files')) { // backup old keys $util->backupAllKeys('recovery'); $newUserPassword = $params['password']; // make sure that the users home is mounted \OC\Files\Filesystem::initMountPoints($user); $keypair = Crypt::createKeypair(); // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; // Save public key $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']); // Encrypt private key empty passphrase $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword); // Save private key $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 $util = new Util($view, $user); $util->recoverUsersFiles($recoveryPassword); } \OC_FileProxy::$enabled = $proxyStatus; } } } }
/** * @medium */ function testGetUserKeys() { $keys = 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 = 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); }
/** * @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 \OCA\Encryption\Hooks::postCreateUser(array('uid' => 'newUser', 'password' => 'newUserPassword')); $this->assertTrue($view->file_exists('public-keys/newUser.public.key')); $this->assertTrue($view->file_exists('newUser/files_encryption/newUser.private.key')); // check if we are able to decrypt the private key $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); $privateKey = \OCA\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\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged')); $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); $this->assertTrue(is_string($privateKey)); // now create a files folder to simulate a already used account $view->mkdir('/newUser/files'); // change the password after the user logged in, now the password should not change \OCA\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged2')); $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged2'); $this->assertFalse($privateKey); $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); $this->assertTrue(is_string($privateKey)); }
/** * @brief enable recovery * * @param $recoveryKeyId * @param $recoveryPassword * @internal param \OCA\Encryption\Util $util * @internal param string $password * @return bool */ public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) { $view = new \OC\Files\View('/'); if ($recoveryKeyId === null) { $recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8); \OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId); } if (!$view->is_dir('/owncloud_private_key')) { $view->mkdir('/owncloud_private_key'); } if (!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key") || !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key")) { $keypair = \OCA\Encryption\Crypt::createKeypair(); \OC_FileProxy::$enabled = false; // Save public key if (!$view->is_dir('/public-keys')) { $view->mkdir('/public-keys'); } $view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']); // Encrypt private key empty passphrase $encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword); // Save private key $view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey); \OC_FileProxy::$enabled = true; // Set recoveryAdmin as enabled \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1); $return = true; } else { // get recovery key and check the password $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); $return = $util->checkRecoveryPassword($recoveryPassword); if ($return) { \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1); } } return $return; }
/** * test webdav put random file */ function testWebdavPUT() { // generate filename $filename = '/tmp-' . uniqid() . '.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/keyfiles/' . $filename . '.key')); // check if shareKey-file was created $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/share-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(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; }
\OCP\JSON::checkAppEnabled('files_encryption'); \OCP\JSON::callCheck(); $l = \OC::$server->getL10N('core'); $return = false; $oldPassword = $_POST['oldPassword']; $newPassword = $_POST['newPassword']; $view = new \OC\Files\View('/'); $session = new \OCA\Encryption\Session($view); $user = \OCP\User::getUser(); $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $keyPath = '/' . $user . '/files_encryption/' . $user . '.private.key'; $encryptedKey = $view->file_get_contents($keyPath); $decryptedKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, $oldPassword); if ($decryptedKey) { $cipher = \OCA\Encryption\Helper::getCipher(); $encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedKey, $newPassword, $cipher); if ($encryptedKey) { \OCA\Encryption\Keymanager::setPrivateKey($encryptedKey, $user); $session->setPrivateKey($decryptedKey); $return = true; } } \OC_FileProxy::$enabled = $proxyStatus; // success or failure if ($return) { $session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL); \OCP\JSON::success(array('data' => array('message' => $l->t('Private key password successfully updated.')))); } else { \OCP\JSON::error(array('data' => array('message' => $l->t('Could not update the private key password. Maybe the old password was not correct.')))); }
* @brief Script to change recovery key password * */ use OCA\Encryption; \OCP\JSON::checkAdminUser(); \OCP\JSON::checkAppEnabled('files_encryption'); \OCP\JSON::callCheck(); $l = OC_L10N::get('core'); $return = false; $oldPassword = $_POST['oldPassword']; $newPassword = $_POST['newPassword']; $view = new \OC\Files\View('/'); $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $keyId = $util->getRecoveryKeyId(); $keyPath = '/owncloud_private_key/' . $keyId . '.private.key'; $encryptedRecoveryKey = $view->file_get_contents($keyPath); $decryptedRecoveryKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedRecoveryKey, $oldPassword); if ($decryptedRecoveryKey) { $encryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword); $view->file_put_contents($keyPath, $encryptedRecoveryKey); $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.')))); }
/** * @large */ function testMultiKeyEncrypt() { # TODO: search in keyfile for actual content as IV will ensure this test always passes $pair1 = Encryption\Crypt::createKeypair(); $this->assertEquals(2, count($pair1)); $this->assertTrue(strlen($pair1['publicKey']) > 1); $this->assertTrue(strlen($pair1['privateKey']) > 1); $crypted = Encryption\Crypt::multiKeyEncrypt($this->dataShort, array($pair1['publicKey'])); $this->assertNotEquals($this->dataShort, $crypted['data']); $decrypt = Encryption\Crypt::multiKeyDecrypt($crypted['data'], $crypted['keys'][0], $pair1['privateKey']); $this->assertEquals($this->dataShort, $decrypt); }
/** * 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 \OCA\Encryption\Session($this->view); // we tried to initialize the encryption app for this session $session->setInitialized(\OCA\Encryption\Session::INIT_EXECUTED); $encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']); $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(\OCA\Encryption\Session::INIT_SUCCESSFUL); return $session; }
/** * @brief Change a user's encryption passphrase * @param array $params keys: uid, password */ public static function setPassphrase($params) { // Only attempt to change passphrase if server-side encryption // is in use (client-side encryption does not have access to // the necessary keys) if (Crypt::mode() === 'server') { if ($params['uid'] === \OCP\User::getUser()) { $view = new \OC_FilesystemView('/'); $session = new \OCA\Encryption\Session($view); // Get existing decrypted private key $privateKey = $session->getPrivateKey(); // Encrypt private key with new user pwd as passphrase $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']); // Save private key Keymanager::setPrivateKey($encryptedPrivateKey); // NOTE: Session does not need to be updated as the // private key has not changed, only the passphrase // used to decrypt it has changed } else { // admin changed the password for a different user, create new keys and reencrypt file keys $user = $params['uid']; $recoveryPassword = $params['recoveryPassword']; $newUserPassword = $params['password']; $view = new \OC_FilesystemView('/'); // make sure that the users home is mounted \OC\Files\Filesystem::initMountPoints($user); $keypair = Crypt::createKeypair(); // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; // Save public key $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']); // Encrypt private key empty passphrase $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword); // Save private key $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 $util = new Util($view, $user); $util->recoverUsersFiles($recoveryPassword); } \OC_FileProxy::$enabled = $proxyStatus; } } }
/** * @brief test generation of legacy encryption key * @depends testLegacyDecryptShort */ function testLegacyCreateKey() { // Create encrypted key $encKey = Encryption\Crypt::legacyCreateKey($this->pass); // Decrypt key $key = Encryption\Crypt::legacyBlockDecrypt($encKey, $this->pass); $this->assertTrue(is_numeric($key)); // Check that key is correct length $this->assertEquals(20, strlen($key)); }