diff options
Diffstat (limited to 'apps/encryption/lib/Crypto')
-rw-r--r-- | apps/encryption/lib/Crypto/Crypt.php | 47 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/DecryptAll.php | 58 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/EncryptAll.php | 151 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/Encryption.php | 67 |
4 files changed, 91 insertions, 232 deletions
diff --git a/apps/encryption/lib/Crypto/Crypt.php b/apps/encryption/lib/Crypto/Crypt.php index 2d212c1f055..463ca4e22bb 100644 --- a/apps/encryption/lib/Crypto/Crypt.php +++ b/apps/encryption/lib/Crypto/Crypt.php @@ -1,34 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Kevin Niehage <kevin@niehage.name> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weiberg <sweiberg@suse.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @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 <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Encryption\Crypto; @@ -108,7 +83,7 @@ class Crypt { /** * create new private/public key-pair for user * - * @return array|bool + * @return array{publicKey: string, privateKey: string}|false */ public function createKeyPair() { $res = $this->getOpenSSLPKey(); @@ -180,7 +155,7 @@ class Crypt { $this->getCipher()); // Create a signature based on the key as well as the current version - $sig = $this->createSignature($encryptedContent, $passPhrase.'_'.$version.'_'.$position); + $sig = $this->createSignature($encryptedContent, $passPhrase . '_' . $version . '_' . $position); // combine content to encrypt the IV identifier and actual IV $catFile = $this->concatIV($encryptedContent, $iv); @@ -482,7 +457,7 @@ class Crypt { if ($enforceSignature) { throw new GenericEncryptionException('Bad Signature', $this->l->t('Bad Signature')); } else { - $this->logger->info("Signature check skipped", ['app' => 'encryption']); + $this->logger->info('Signature check skipped', ['app' => 'encryption']); } } } @@ -776,7 +751,7 @@ class Crypt { $result = false; // check if RC4 is used - if (strcasecmp($cipher_algo, "rc4") === 0) { + if (strcasecmp($cipher_algo, 'rc4') === 0) { // decrypt the intermediate key with RSA if (openssl_private_decrypt($encrypted_key, $intermediate, $private_key, OPENSSL_PKCS1_PADDING)) { // decrypt the file key with the intermediate key @@ -785,7 +760,7 @@ class Crypt { $result = (strlen($output) === strlen($data)); } } else { - throw new DecryptionFailedException('Unsupported cipher '.$cipher_algo); + throw new DecryptionFailedException('Unsupported cipher ' . $cipher_algo); } return $result; @@ -801,7 +776,7 @@ class Crypt { $result = false; // check if RC4 is used - if (strcasecmp($cipher_algo, "rc4") === 0) { + if (strcasecmp($cipher_algo, 'rc4') === 0) { // make sure that there is at least one public key to use if (count($public_key) >= 1) { // generate the intermediate key @@ -832,7 +807,7 @@ class Crypt { } } } else { - throw new EncryptionFailedException('Unsupported cipher '.$cipher_algo); + throw new EncryptionFailedException('Unsupported cipher ' . $cipher_algo); } return $result; diff --git a/apps/encryption/lib/Crypto/DecryptAll.php b/apps/encryption/lib/Crypto/DecryptAll.php index e982da72086..362f43b8672 100644 --- a/apps/encryption/lib/Crypto/DecryptAll.php +++ b/apps/encryption/lib/Crypto/DecryptAll.php @@ -1,27 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Encryption\Crypto; +use OCA\Encryption\Exceptions\PrivateKeyMissingException; use OCA\Encryption\KeyManager; use OCA\Encryption\Session; use OCA\Encryption\Util; @@ -33,21 +19,6 @@ use Symfony\Component\Console\Question\Question; class DecryptAll { - /** @var Util */ - protected $util; - - /** @var QuestionHelper */ - protected $questionHelper; - - /** @var Crypt */ - protected $crypt; - - /** @var KeyManager */ - protected $keyManager; - - /** @var Session */ - protected $session; - /** * @param Util $util * @param KeyManager $keyManager @@ -56,17 +27,12 @@ class DecryptAll { * @param QuestionHelper $questionHelper */ public function __construct( - Util $util, - KeyManager $keyManager, - Crypt $crypt, - Session $session, - QuestionHelper $questionHelper + protected Util $util, + protected KeyManager $keyManager, + protected Crypt $crypt, + protected Session $session, + protected QuestionHelper $questionHelper, ) { - $this->util = $util; - $this->keyManager = $keyManager; - $this->crypt = $crypt; - $this->session = $session; - $this->questionHelper = $questionHelper; } /** @@ -88,7 +54,7 @@ class DecryptAll { $recoveryKeyId = $this->keyManager->getRecoveryKeyId(); if (!empty($user)) { $output->writeln('You can only decrypt the users files if you know'); - $output->writeln('the users password or if he activated the recovery key.'); + $output->writeln('the users password or if they activated the recovery key.'); $output->writeln(''); $questionUseLoginPassword = new ConfirmationQuestion( 'Do you want to use the users login password to decrypt all files? (y/n) ', @@ -133,7 +99,7 @@ class DecryptAll { * @param string $user * @param string $password * @return bool|string - * @throws \OCA\Encryption\Exceptions\PrivateKeyMissingException + * @throws PrivateKeyMissingException */ protected function getPrivateKey($user, $password) { $recoveryKeyId = $this->keyManager->getRecoveryKeyId(); diff --git a/apps/encryption/lib/Crypto/EncryptAll.php b/apps/encryption/lib/Crypto/EncryptAll.php index 5420e729898..4ed75b85a93 100644 --- a/apps/encryption/lib/Crypto/EncryptAll.php +++ b/apps/encryption/lib/Crypto/EncryptAll.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Kenneth Newwood <kenneth@newwood.name> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @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 <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Encryption\Crypto; @@ -32,6 +12,7 @@ use OC\Files\View; use OCA\Encryption\KeyManager; use OCA\Encryption\Users\Setup; use OCA\Encryption\Util; +use OCP\Files\FileInfo; use OCP\IConfig; use OCP\IL10N; use OCP\IUser; @@ -40,6 +21,7 @@ use OCP\L10N\IFactory; use OCP\Mail\Headers\AutoSubmitted; use OCP\Mail\IMailer; use OCP\Security\ISecureRandom; +use Psr\Log\LoggerInterface; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Helper\Table; @@ -49,72 +31,29 @@ use Symfony\Component\Console\Question\ConfirmationQuestion; class EncryptAll { - /** @var Setup */ - protected $userSetup; - - /** @var IUserManager */ - protected $userManager; - - /** @var View */ - protected $rootView; - - /** @var KeyManager */ - protected $keyManager; - - /** @var Util */ - protected $util; - - /** @var array */ + /** @var array */ protected $userPasswords; - /** @var IConfig */ - protected $config; - - /** @var IMailer */ - protected $mailer; - - /** @var IL10N */ - protected $l; - - /** @var IFactory */ - protected $l10nFactory; - - /** @var QuestionHelper */ - protected $questionHelper; - - /** @var OutputInterface */ + /** @var OutputInterface */ protected $output; - /** @var InputInterface */ + /** @var InputInterface */ protected $input; - /** @var ISecureRandom */ - protected $secureRandom; - public function __construct( - Setup $userSetup, - IUserManager $userManager, - View $rootView, - KeyManager $keyManager, - Util $util, - IConfig $config, - IMailer $mailer, - IL10N $l, - IFactory $l10nFactory, - QuestionHelper $questionHelper, - ISecureRandom $secureRandom + protected Setup $userSetup, + protected IUserManager $userManager, + protected View $rootView, + protected KeyManager $keyManager, + protected Util $util, + protected IConfig $config, + protected IMailer $mailer, + protected IL10N $l, + protected IFactory $l10nFactory, + protected QuestionHelper $questionHelper, + protected ISecureRandom $secureRandom, + protected LoggerInterface $logger, ) { - $this->userSetup = $userSetup; - $this->userManager = $userManager; - $this->rootView = $rootView; - $this->keyManager = $keyManager; - $this->util = $util; - $this->config = $config; - $this->mailer = $mailer; - $this->l = $l; - $this->l10nFactory = $l10nFactory; - $this->questionHelper = $questionHelper; - $this->secureRandom = $secureRandom; // store one time passwords for the users $this->userPasswords = []; } @@ -223,7 +162,7 @@ class EncryptAll { $userNo++; } } - $progress->setMessage("all files encrypted"); + $progress->setMessage('all files encrypted'); $progress->finish(); } @@ -264,33 +203,42 @@ class EncryptAll { while ($root = array_pop($directories)) { $content = $this->rootView->getDirectoryContent($root); foreach ($content as $file) { - $path = $root . '/' . $file['name']; - if ($this->rootView->is_dir($path)) { + $path = $root . '/' . $file->getName(); + if ($file->isShared()) { + $progress->setMessage("Skip shared file/folder $path"); + $progress->advance(); + continue; + } elseif ($file->getType() === FileInfo::TYPE_FOLDER) { $directories[] = $path; continue; } else { $progress->setMessage("encrypt files for user $userCount: $path"); $progress->advance(); - if ($this->encryptFile($path) === false) { - $progress->setMessage("encrypt files for user $userCount: $path (already encrypted)"); + try { + if ($this->encryptFile($file, $path) === false) { + $progress->setMessage("encrypt files for user $userCount: $path (already encrypted)"); + $progress->advance(); + } + } catch (\Exception $e) { + $progress->setMessage("Failed to encrypt path $path: " . $e->getMessage()); $progress->advance(); + $this->logger->error( + 'Failed to encrypt path {path}', + [ + 'user' => $uid, + 'path' => $path, + 'exception' => $e, + ] + ); } } } } } - /** - * encrypt file - * - * @param string $path - * @return bool - */ - protected function encryptFile($path) { - + protected function encryptFile(FileInfo $fileInfo, string $path): bool { // skip already encrypted files - $fileInfo = $this->rootView->getFileInfo($path); - if ($fileInfo !== false && $fileInfo->isEncrypted()) { + if ($fileInfo->isEncrypted()) { return true; } @@ -298,7 +246,14 @@ class EncryptAll { $target = $path . '.encrypted.' . time(); try { - $this->rootView->copy($source, $target); + $copySuccess = $this->rootView->copy($source, $target); + if ($copySuccess === false) { + /* Copy failed, abort */ + if ($this->rootView->file_exists($target)) { + $this->rootView->unlink($target); + } + throw new \Exception('Copy failed for ' . $source); + } $this->rootView->rename($target, $source); } catch (DecryptionFailedException $e) { if ($this->rootView->file_exists($target)) { diff --git a/apps/encryption/lib/Crypto/Encryption.php b/apps/encryption/lib/Crypto/Encryption.php index 1481d3a9a23..6d388624e48 100644 --- a/apps/encryption/lib/Crypto/Encryption.php +++ b/apps/encryption/lib/Crypto/Encryption.php @@ -1,41 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * - * @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 <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Encryption\Crypto; use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Files\Cache\Scanner; use OC\Files\View; +use OCA\Encryption\Exceptions\MultiKeyEncryptException; use OCA\Encryption\Exceptions\PublicKeyMissingException; use OCA\Encryption\KeyManager; use OCA\Encryption\Session; @@ -80,8 +55,6 @@ class Encryption implements IEncryptionModule { /** @var int Current version of the file */ private int $version = 0; - private bool $useLegacyFileKey = true; - /** @var array remember encryption signature version */ private static $rememberVersion = []; @@ -127,8 +100,8 @@ class Encryption implements IEncryptionModule { * @param array $accessList who has access to the file contains the key 'users' and 'public' * * @return array $header contain data as key-value pairs which should be - * written to the header, in case of a write operation - * or if no additional data is needed return a empty array + * written to the header, in case of a write operation + * or if no additional data is needed return a empty array */ public function begin($path, $user, $mode, array $header, array $accessList) { $this->path = $this->getPathToRealFile($path); @@ -138,7 +111,6 @@ class Encryption implements IEncryptionModule { $this->writeCache = ''; $this->useLegacyBase64Encoding = true; - $this->useLegacyFileKey = ($header['useLegacyFileKey'] ?? 'true') !== 'false'; if (isset($header['encoding'])) { $this->useLegacyBase64Encoding = $header['encoding'] !== Crypt::BINARY_ENCODING_FORMAT; @@ -152,19 +124,10 @@ class Encryption implements IEncryptionModule { } } - if ($this->session->decryptAllModeActivated()) { - $shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid()); - if ($this->useLegacyFileKey) { - $encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path); - $this->fileKey = $this->crypt->multiKeyDecryptLegacy($encryptedFileKey, - $shareKey, - $this->session->getDecryptAllKey()); - } else { - $this->fileKey = $this->crypt->multiKeyDecrypt($shareKey, $this->session->getDecryptAllKey()); - } - } else { - $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user, $this->useLegacyFileKey); - } + /* If useLegacyFileKey is not specified in header, auto-detect, to be safe */ + $useLegacyFileKey = (($header['useLegacyFileKey'] ?? '') == 'false' ? false : null); + + $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user, $useLegacyFileKey, $this->session->decryptAllModeActivated()); // always use the version from the original file, also part files // need to have a correct version number if they get moved over to the @@ -225,7 +188,7 @@ class Encryption implements IEncryptionModule { * of a write operation * @throws PublicKeyMissingException * @throws \Exception - * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException + * @throws MultiKeyEncryptException */ public function end($path, $position = '0') { $result = ''; @@ -470,7 +433,7 @@ class Encryption implements IEncryptionModule { * e.g. if all encryption keys exists * * @param string $path - * @param string $uid user for whom we want to check if he can read the file + * @param string $uid user for whom we want to check if they can read the file * @return bool * @throws DecryptionFailedException */ @@ -483,8 +446,8 @@ class Encryption implements IEncryptionModule { // error message because in this case it means that the file was // shared with the user at a point where the user didn't had a // valid private/public key - $msg = 'Encryption module "' . $this->getDisplayName() . - '" is not able to read ' . $path; + $msg = 'Encryption module "' . $this->getDisplayName() + . '" is not able to read ' . $path; $hint = $this->l->t('Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you.'); $this->logger->warning($msg); throw new DecryptionFailedException($msg, $hint); |