diff options
author | Côme Chilliet <come.chilliet@nextcloud.com> | 2023-03-16 14:53:51 +0100 |
---|---|---|
committer | Côme Chilliet <come.chilliet@nextcloud.com> | 2023-03-17 11:08:58 +0100 |
commit | 8900d030d1a6359a0b58b7257e3a3fd33db4a6a4 (patch) | |
tree | 5e32030a28cc1fb245d38098da7d84cb93a25e5d /apps | |
parent | fbe282caeb7dd0d91435f6f547db027e500e248a (diff) | |
download | nextcloud-server-8900d030d1a6359a0b58b7257e3a3fd33db4a6a4.tar.gz nextcloud-server-8900d030d1a6359a0b58b7257e3a3fd33db4a6a4.zip |
Adapt code to new encryption system
fileKey gets deleted upon save as it’s stored in shareKeys instead now.
We use presence of a fileKey to detect if a file is using the legacy
system or the new one, because we do not always have access to header
data.
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/encryption/lib/Crypto/Crypt.php | 15 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/Encryption.php | 28 | ||||
-rw-r--r-- | apps/encryption/lib/KeyManager.php | 18 | ||||
-rw-r--r-- | apps/encryption/lib/Recovery.php | 51 | ||||
-rw-r--r-- | apps/encryption/tests/Crypto/EncryptionTest.php | 6 |
5 files changed, 56 insertions, 62 deletions
diff --git a/apps/encryption/lib/Crypto/Crypt.php b/apps/encryption/lib/Crypto/Crypt.php index f4544a61716..516164c6a80 100644 --- a/apps/encryption/lib/Crypto/Crypt.php +++ b/apps/encryption/lib/Crypto/Crypt.php @@ -185,14 +185,9 @@ class Crypt { } /** - * @param string $plainContent - * @param string $passPhrase - * @param int $version - * @param int $position - * @return false|string * @throws EncryptionFailedException */ - public function symmetricEncryptFileContent($plainContent, $passPhrase, $version, $position) { + public function symmetricEncryptFileContent(string $plainContent, string $passPhrase, int $version, string $position): string|false { if (!$plainContent) { $this->logger->error('Encryption Library, symmetrical encryption failed no content given', ['app' => 'encryption']); @@ -409,7 +404,7 @@ class Crypt { $privateKey, $hash, 0, - 0 + '0' ); return $encryptedKey; @@ -537,12 +532,8 @@ class Crypt { /** * create signature - * - * @param string $data - * @param string $passPhrase - * @return string */ - private function createSignature($data, $passPhrase) { + private function createSignature(string $data, string $passPhrase): string { $passPhrase = hash('sha512', $passPhrase . 'a', true); return hash_hmac('sha256', $data, $passPhrase); } diff --git a/apps/encryption/lib/Crypto/Encryption.php b/apps/encryption/lib/Crypto/Encryption.php index bee91e530ff..838c63e9495 100644 --- a/apps/encryption/lib/Crypto/Encryption.php +++ b/apps/encryption/lib/Crypto/Encryption.php @@ -266,14 +266,14 @@ class Encryption implements IEncryptionModule { * buffer. * * @param string $path to the file - * @param int $position + * @param string $position * @return string remained data which should be written to the file in case * of a write operation * @throws PublicKeyMissingException * @throws \Exception * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException */ - public function end($path, $position = 0) { + public function end($path, $position = '0') { $result = ''; if ($this->isWriteOperation) { // in case of a part file we remember the new signature versions @@ -308,11 +308,13 @@ class Encryption implements IEncryptionModule { } $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys, $this->getOwner($path)); - //TODO adapt this to new return of the method, same for other calls of multiKeyEncrypt - $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys); - $this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles); + $shareKeys = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys); + $this->keyManager->deleteLegacyFileKey($this->path); + foreach ($shareKeys as $uid => $keyFile) { + $this->keyManager->setShareKey($this->path, $uid, $keyFile); + } } - return $result; + return $result ?: ''; } @@ -362,7 +364,7 @@ class Encryption implements IEncryptionModule { // Read the chunk from the start of $data $chunk = substr($data, 0, $this->getUnencryptedBlockSize(true)); - $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, $position); + $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, (string)$position); // Remove the chunk we just processed from // $data, leaving only unprocessed data in $data @@ -400,7 +402,7 @@ class Encryption implements IEncryptionModule { * @param string $path path to the file which should be updated * @param string $uid of the user who performs the operation * @param array $accessList who has access to the file contains the key 'users' and 'public' - * @return boolean + * @return bool */ public function update($path, $uid, array $accessList) { if (empty($accessList)) { @@ -408,10 +410,10 @@ class Encryption implements IEncryptionModule { $this->keyManager->setVersion($path, self::$rememberVersion[$path], new View()); unset(self::$rememberVersion[$path]); } - return; + return false; } - $fileKey = $this->keyManager->getFileKey($path, $uid); + $fileKey = $this->keyManager->getFileKey($path, $uid, null); if (!empty($fileKey)) { $publicKeys = []; @@ -429,11 +431,13 @@ class Encryption implements IEncryptionModule { $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->getOwner($path)); - $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $shareKeys = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); $this->keyManager->deleteAllFileKeys($path); - $this->keyManager->setAllFileKeys($path, $encryptedFileKey); + foreach ($shareKeys as $uid => $keyFile) { + $this->keyManager->setShareKey($this->path, $uid, $keyFile); + } } else { $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted', ['file' => $path, 'app' => 'encryption']); diff --git a/apps/encryption/lib/KeyManager.php b/apps/encryption/lib/KeyManager.php index 5f35f7a8422..5c933b5f8b2 100644 --- a/apps/encryption/lib/KeyManager.php +++ b/apps/encryption/lib/KeyManager.php @@ -440,18 +440,19 @@ class KeyManager { /** * @param string $path * @param $uid + * @param ?bool $useLegacyFileKey null means try both * @return string */ - public function getFileKey(string $path, ?string $uid, bool $useLegacyFileKey): string { + public function getFileKey(string $path, ?string $uid, ?bool $useLegacyFileKey): string { if ($uid === '') { $uid = null; } $publicAccess = is_null($uid); - - if ($useLegacyFileKey) { + $encryptedFileKey = ''; + if ($useLegacyFileKey ?? true) { $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID); - if (empty($encryptedFileKey)) { + if (empty($encryptedFileKey) && $useLegacyFileKey) { return ''; } } @@ -477,13 +478,14 @@ class KeyManager { $privateKey = $this->session->getPrivateKey(); } - if ($useLegacyFileKey) { + if ($useLegacyFileKey ?? true) { if ($encryptedFileKey && $shareKey && $privateKey) { return $this->crypt->multiKeyDecryptLegacy($encryptedFileKey, $shareKey, $privateKey); } - } else { + } + if ($useLegacyFileKey ?? false) { if ($shareKey && $privateKey) { return $this->crypt->multiKeyDecrypt($shareKey, $privateKey); } @@ -664,6 +666,10 @@ class KeyManager { return $this->keyStorage->deleteAllFileKeys($path); } + public function deleteLegacyFileKey(string $path): bool { + return $this->keyStorage->deleteFileKey($path, $this->fileKeyId, Encryption::ID); + } + /** * @param array $userIds * @return array diff --git a/apps/encryption/lib/Recovery.php b/apps/encryption/lib/Recovery.php index f4336ec7c4e..25738dabf89 100644 --- a/apps/encryption/lib/Recovery.php +++ b/apps/encryption/lib/Recovery.php @@ -35,8 +35,6 @@ use OCP\IUserSession; use OCP\PreConditionNotMetException; class Recovery { - - /** * @var null|IUser */ @@ -102,7 +100,7 @@ class Recovery { } if ($keyManager->checkRecoveryPassword($password)) { - $appConfig->setAppValue('encryption', 'recoveryAdminEnabled', 1); + $appConfig->setAppValue('encryption', 'recoveryAdminEnabled', '1'); return true; } @@ -140,7 +138,7 @@ class Recovery { if ($keyManager->checkRecoveryPassword($recoveryPassword)) { // Set recoveryAdmin as disabled - $this->config->setAppValue('encryption', 'recoveryAdminEnabled', 0); + $this->config->setAppValue('encryption', 'recoveryAdminEnabled', '0'); return true; } return false; @@ -169,7 +167,7 @@ class Recovery { * @return bool */ public function isRecoveryKeyEnabled() { - $enabled = $this->config->getAppValue('encryption', 'recoveryAdminEnabled', 0); + $enabled = $this->config->getAppValue('encryption', 'recoveryAdminEnabled', '0'); return ($enabled === '1'); } @@ -199,16 +197,15 @@ class Recovery { /** * add recovery key to all encrypted files - * @param string $path */ - private function addRecoveryKeys($path) { + private function addRecoveryKeys(string $path): void { $dirContent = $this->view->getDirectoryContent($path); foreach ($dirContent as $item) { $filePath = $item->getPath(); if ($item['type'] === 'dir') { $this->addRecoveryKeys($filePath . '/'); } else { - $fileKey = $this->keyManager->getFileKey($filePath, $this->user->getUID()); + $fileKey = $this->keyManager->getFileKey($filePath, $this->user->getUID(), null); if (!empty($fileKey)) { $accessList = $this->file->getAccessList($filePath); $publicKeys = []; @@ -218,8 +215,11 @@ class Recovery { $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->user->getUID()); - $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); - $this->keyManager->setAllFileKeys($filePath, $encryptedKeyfiles); + $shareKeys = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $this->keyManager->deleteLegacyFileKey($filePath); + foreach ($shareKeys as $uid => $keyFile) { + $this->keyManager->setShareKey($filePath, $uid, $keyFile); + } } } } @@ -227,9 +227,8 @@ class Recovery { /** * remove recovery key to all encrypted files - * @param string $path */ - private function removeRecoveryKeys($path) { + private function removeRecoveryKeys(string $path): void { $dirContent = $this->view->getDirectoryContent($path); foreach ($dirContent as $item) { $filePath = $item->getPath(); @@ -243,11 +242,8 @@ class Recovery { /** * recover users files with the recovery key - * - * @param string $recoveryPassword - * @param string $user */ - public function recoverUsersFiles($recoveryPassword, $user) { + public function recoverUsersFiles(string $recoveryPassword, string $user): void { $encryptedKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId()); $privateKey = $this->crypt->decryptPrivateKey($encryptedKey, $recoveryPassword); @@ -258,12 +254,8 @@ class Recovery { /** * recover users files - * - * @param string $path - * @param string $privateKey - * @param string $uid */ - private function recoverAllFiles($path, $privateKey, $uid) { + private function recoverAllFiles(string $path, string $privateKey, string $uid): void { $dirContent = $this->view->getDirectoryContent($path); foreach ($dirContent as $item) { @@ -279,19 +271,17 @@ class Recovery { /** * recover file - * - * @param string $path - * @param string $privateKey - * @param string $uid */ - private function recoverFile($path, $privateKey, $uid) { + private function recoverFile(string $path, string $privateKey, string $uid): void { $encryptedFileKey = $this->keyManager->getEncryptedFileKey($path); $shareKey = $this->keyManager->getShareKey($path, $this->keyManager->getRecoveryKeyId()); if ($encryptedFileKey && $shareKey && $privateKey) { - $fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey, + $fileKey = $this->crypt->multiKeyDecryptLegacy($encryptedFileKey, $shareKey, $privateKey); + } elseif ($shareKey && $privateKey) { + $fileKey = $this->crypt->multiKeyDecrypt($shareKey, $privateKey); } if (!empty($fileKey)) { @@ -303,8 +293,11 @@ class Recovery { $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $uid); - $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); - $this->keyManager->setAllFileKeys($path, $encryptedKeyfiles); + $shareKeys = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $this->keyManager->deleteLegacyFileKey($path); + foreach ($shareKeys as $uid => $keyFile) { + $this->keyManager->setShareKey($path, $uid, $keyFile); + } } } } diff --git a/apps/encryption/tests/Crypto/EncryptionTest.php b/apps/encryption/tests/Crypto/EncryptionTest.php index 63698dcdc63..675a8bf8e29 100644 --- a/apps/encryption/tests/Crypto/EncryptionTest.php +++ b/apps/encryption/tests/Crypto/EncryptionTest.php @@ -42,7 +42,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; class EncryptionTest extends TestCase { - /** @var Encryption */ private $instance; @@ -156,7 +155,7 @@ class EncryptionTest extends TestCase { ->willReturnCallback([$this, 'addSystemKeysCallback']); $this->cryptMock->expects($this->any()) ->method('multiKeyEncrypt') - ->willReturn(true); + ->willReturn([]); $this->instance->end('/foo/bar'); } @@ -276,7 +275,7 @@ class EncryptionTest extends TestCase { ->with($path, $recoveryKeyId) ->willReturn($recoveryShareKey); $this->cryptMock->expects($this->once()) - ->method('multiKeyDecrypt') + ->method('multiKeyDecryptLegacy') ->with('encryptedFileKey', $recoveryShareKey, $decryptAllKey) ->willReturn($fileKey); @@ -378,6 +377,7 @@ class EncryptionTest extends TestCase { function ($fileKey, $publicKeys) { $this->assertEmpty($publicKeys); $this->assertSame('fileKey', $fileKey); + return []; } ); |