diff options
author | Côme Chilliet <come.chilliet@nextcloud.com> | 2023-03-15 18:10:24 +0100 |
---|---|---|
committer | Côme Chilliet <come.chilliet@nextcloud.com> | 2023-03-17 11:08:50 +0100 |
commit | fbe282caeb7dd0d91435f6f547db027e500e248a (patch) | |
tree | f74b86c473a4f3d996647e2d4671a9ec20931691 /apps/encryption | |
parent | c7c1133c15f4ab33a69d40bb387354a270ca5541 (diff) | |
download | nextcloud-server-fbe282caeb7dd0d91435f6f547db027e500e248a.tar.gz nextcloud-server-fbe282caeb7dd0d91435f6f547db027e500e248a.zip |
Getting rid of openssl_seal and rc4 in server side encryption
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Diffstat (limited to 'apps/encryption')
-rw-r--r-- | apps/encryption/lib/Crypto/Crypt.php | 63 | ||||
-rw-r--r-- | apps/encryption/lib/Crypto/Encryption.php | 29 | ||||
-rw-r--r-- | apps/encryption/lib/KeyManager.php | 26 |
3 files changed, 94 insertions, 24 deletions
diff --git a/apps/encryption/lib/Crypto/Crypt.php b/apps/encryption/lib/Crypto/Crypt.php index fe9813a6cfa..f4544a61716 100644 --- a/apps/encryption/lib/Crypto/Crypt.php +++ b/apps/encryption/lib/Crypto/Crypt.php @@ -695,13 +695,25 @@ class Crypt { } /** - * @param string $encKeyFile - * @param string $shareKey * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $privateKey - * @return string * @throws MultiKeyDecryptException */ - public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) { + public function multiKeyDecrypt(string $shareKey, $privateKey): string { + $plainContent = ''; + + // decrypt the intermediate key with RSA + if (openssl_private_decrypt($shareKey, $intermediate, $privateKey, OPENSSL_PKCS1_OAEP_PADDING)) { + return $intermediate; + } else { + throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string()); + } + } + + /** + * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $privateKey + * @throws MultiKeyDecryptException + */ + public function multiKeyDecryptLegacy(string $encKeyFile, string $shareKey, $privateKey): string { if (!$encKeyFile) { throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content'); } @@ -715,12 +727,53 @@ class Crypt { } /** + * @throws MultiKeyEncryptException + */ + public function multiKeyEncrypt(string $plainContent, array $keyFiles): array { + if (empty($plainContent)) { + throw new MultiKeyEncryptException('Cannot multikeyencrypt empty plain content'); + } + + // Set empty vars to be set by openssl by reference + $shareKeys = []; + $mappedShareKeys = []; + + // make sure that there is at least one public key to use + if (count($keyFiles) >= 1) { + // prepare the encrypted keys + $shareKeys = []; + + // iterate over the public keys and encrypt the intermediate + // for each of them with RSA + foreach ($keyFiles as $tmp_key) { + if (openssl_public_encrypt($plainContent, $tmp_output, $tmp_key, OPENSSL_PKCS1_OAEP_PADDING)) { + $shareKeys[] = $tmp_output; + } + } + + // set the result if everything worked fine + if (count($keyFiles) === count($shareKeys)) { + $i = 0; + + // Ensure each shareKey is labelled with its corresponding key id + foreach ($keyFiles as $userId => $publicKey) { + $mappedShareKeys[$userId] = $shareKeys[$i]; + $i++; + } + + return $mappedShareKeys; + } + } + throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string()); + } + + /** * @param string $plainContent * @param array $keyFiles * @return array * @throws MultiKeyEncryptException */ - public function multiKeyEncrypt($plainContent, array $keyFiles) { + public function multiKeyEncryptLegacy($plainContent, array $keyFiles) { // openssl_seal returns false without errors if plaincontent is empty // so trigger our own error if (empty($plainContent)) { diff --git a/apps/encryption/lib/Crypto/Encryption.php b/apps/encryption/lib/Crypto/Encryption.php index b44472fd04a..bee91e530ff 100644 --- a/apps/encryption/lib/Crypto/Encryption.php +++ b/apps/encryption/lib/Crypto/Encryption.php @@ -108,6 +108,8 @@ class Encryption implements IEncryptionModule { /** @var int Current version of the file */ private $version = 0; + private bool $useLegacyFileKey = true; + /** @var array remember encryption signature version */ private static $rememberVersion = []; @@ -182,6 +184,8 @@ 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; } @@ -195,13 +199,17 @@ class Encryption implements IEncryptionModule { } if ($this->session->decryptAllModeActivated()) { - $encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path); $shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid()); - $this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey, - $shareKey, - $this->session->getDecryptAllKey()); + 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->fileKey = $this->keyManager->getFileKey($this->path, $this->user, $this->useLegacyFileKey); } // always use the version from the original file, also part files @@ -239,7 +247,11 @@ class Encryption implements IEncryptionModule { $this->cipher = $this->crypt->getLegacyCipher(); } - $result = ['cipher' => $this->cipher, 'signed' => 'true']; + $result = [ + 'cipher' => $this->cipher, + 'signed' => 'true', + 'useLegacyFileKey' => 'false', + ]; if ($this->useLegacyBase64Encoding !== true) { $result['encoding'] = Crypt::BINARY_ENCODING_FORMAT; @@ -296,6 +308,7 @@ 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); } @@ -315,7 +328,6 @@ class Encryption implements IEncryptionModule { // If extra data is left over from the last round, make sure it // is integrated into the next block if ($this->writeCache) { - // Concat writeCache to start of $data $data = $this->writeCache . $data; @@ -327,7 +339,6 @@ class Encryption implements IEncryptionModule { $encrypted = ''; // While there still remains some data to be processed & written while (strlen($data) > 0) { - // Remaining length for this iteration, not of the // entire file (may be greater than 8192 bytes) $remainingLength = strlen($data); @@ -335,7 +346,6 @@ class Encryption implements IEncryptionModule { // If data remaining to be written is less than the // size of 1 unencrypted block if ($remainingLength < $this->getUnencryptedBlockSize(true)) { - // Set writeCache to contents of $data // The writeCache will be carried over to the // next write round, and added to the start of @@ -349,7 +359,6 @@ class Encryption implements IEncryptionModule { // Clear $data ready for next round $data = ''; } else { - // Read the chunk from the start of $data $chunk = substr($data, 0, $this->getUnencryptedBlockSize(true)); diff --git a/apps/encryption/lib/KeyManager.php b/apps/encryption/lib/KeyManager.php index 2c6487d062a..5f35f7a8422 100644 --- a/apps/encryption/lib/KeyManager.php +++ b/apps/encryption/lib/KeyManager.php @@ -44,7 +44,6 @@ use OCP\IUserSession; use OCP\Lock\ILockingProvider; class KeyManager { - /** * @var Session */ @@ -443,15 +442,18 @@ class KeyManager { * @param $uid * @return string */ - public function getFileKey($path, $uid) { + public function getFileKey(string $path, ?string $uid, bool $useLegacyFileKey): string { if ($uid === '') { $uid = null; } $publicAccess = is_null($uid); - $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID); - if (empty($encryptedFileKey)) { - return ''; + if ($useLegacyFileKey) { + $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID); + + if (empty($encryptedFileKey)) { + return ''; + } } if ($this->util->isMasterKeyEnabled()) { @@ -475,10 +477,16 @@ class KeyManager { $privateKey = $this->session->getPrivateKey(); } - if ($encryptedFileKey && $shareKey && $privateKey) { - return $this->crypt->multiKeyDecrypt($encryptedFileKey, - $shareKey, - $privateKey); + if ($useLegacyFileKey) { + if ($encryptedFileKey && $shareKey && $privateKey) { + return $this->crypt->multiKeyDecryptLegacy($encryptedFileKey, + $shareKey, + $privateKey); + } + } else { + if ($shareKey && $privateKey) { + return $this->crypt->multiKeyDecrypt($shareKey, $privateKey); + } } return ''; |