diff options
author | Simon L <szaimen@e.mail.de> | 2023-03-02 17:15:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-02 17:15:45 +0100 |
commit | 0df20f6ebecef17a97f0b8afb6f5fc5b99e915a6 (patch) | |
tree | 2fa862c135089a02ccf89bdaf75b89701fc2122a /apps | |
parent | 6466c8ee31f3b30b8710c91c05d4b88d1d7ef086 (diff) | |
parent | f2912ce8bcd4316b587e249d3deb94c461caddaf (diff) | |
download | nextcloud-server-0df20f6ebecef17a97f0b8afb6f5fc5b99e915a6.tar.gz nextcloud-server-0df20f6ebecef17a97f0b8afb6f5fc5b99e915a6.zip |
Merge pull request #36173 from nextcloud/enh/openssl-seal-custom
Use a PHP implementation of openssl_seal that allows to use modern ciphers
Diffstat (limited to 'apps')
-rw-r--r-- | apps/encryption/lib/Crypto/Crypt.php | 117 |
1 files changed, 108 insertions, 9 deletions
diff --git a/apps/encryption/lib/Crypto/Crypt.php b/apps/encryption/lib/Crypto/Crypt.php index efb5a6868b0..fe9813a6cfa 100644 --- a/apps/encryption/lib/Crypto/Crypt.php +++ b/apps/encryption/lib/Crypto/Crypt.php @@ -6,7 +6,9 @@ * @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> @@ -40,6 +42,7 @@ use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; use OCP\IUserSession; +use phpseclib\Crypt\RC4; /** * Class Crypt provides the encryption implementation of the default Nextcloud @@ -517,12 +520,9 @@ class Crypt { /** * check for valid signature * - * @param string $data - * @param string $passPhrase - * @param string $expectedSignature * @throws GenericEncryptionException */ - private function checkSignature($data, $passPhrase, $expectedSignature) { + private function checkSignature(string $data, string $passPhrase, string $expectedSignature): void { $enforceSignature = !$this->config->getSystemValueBool('encryption_skip_signature_check', false); $signature = $this->createSignature($data, $passPhrase); @@ -695,9 +695,9 @@ class Crypt { } /** - * @param $encKeyFile - * @param $shareKey - * @param $privateKey + * @param string $encKeyFile + * @param string $shareKey + * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $privateKey * @return string * @throws MultiKeyDecryptException */ @@ -706,7 +706,8 @@ class Crypt { throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content'); } - if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) { + $plainContent = ''; + if ($this->opensslOpen($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) { return $plainContent; } else { throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string()); @@ -731,7 +732,7 @@ class Crypt { $shareKeys = []; $mappedShareKeys = []; - if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles, 'RC4')) { + if ($this->opensslSeal($plainContent, $sealed, $shareKeys, $keyFiles, 'RC4')) { $i = 0; // Ensure each shareKey is labelled with its corresponding key id @@ -749,7 +750,105 @@ class Crypt { } } + /** + * returns the value of $useLegacyBase64Encoding + * + * @return bool + */ public function useLegacyBase64Encoding(): bool { return $this->useLegacyBase64Encoding; } + + /** + * Uses phpseclib RC4 implementation + */ + private function rc4Decrypt(string $data, string $secret): string { + $rc4 = new RC4(); + /** @psalm-suppress InternalMethod */ + $rc4->setKey($secret); + + return $rc4->decrypt($data); + } + + /** + * Uses phpseclib RC4 implementation + */ + private function rc4Encrypt(string $data, string $secret): string { + $rc4 = new RC4(); + /** @psalm-suppress InternalMethod */ + $rc4->setKey($secret); + + return $rc4->encrypt($data); + } + + /** + * Custom implementation of openssl_open() + * + * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $private_key + * @throws DecryptionFailedException + */ + private function opensslOpen(string $data, string &$output, string $encrypted_key, $private_key, string $cipher_algo): bool { + $result = false; + + // check if RC4 is used + 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 + // using our own RC4 implementation + $output = $this->rc4Decrypt($data, $intermediate); + $result = (strlen($output) === strlen($data)); + } + } else { + throw new DecryptionFailedException('Unsupported cipher '.$cipher_algo); + } + + return $result; + } + + /** + * Custom implementation of openssl_seal() + * + * @throws EncryptionFailedException + */ + private function opensslSeal(string $data, string &$sealed_data, array &$encrypted_keys, array $public_key, string $cipher_algo): int|false { + $result = false; + + // check if RC4 is used + 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 + $intermediate = openssl_random_pseudo_bytes(16, $strong_result); + + // check if we got strong random data + if ($strong_result) { + // encrypt the file key with the intermediate key + // using our own RC4 implementation + $sealed_data = $this->rc4Encrypt($data, $intermediate); + if (strlen($sealed_data) === strlen($data)) { + // prepare the encrypted keys + $encrypted_keys = []; + + // iterate over the public keys and encrypt the intermediate + // for each of them with RSA + foreach ($public_key as $tmp_key) { + if (openssl_public_encrypt($intermediate, $tmp_output, $tmp_key, OPENSSL_PKCS1_PADDING)) { + $encrypted_keys[] = $tmp_output; + } + } + + // set the result if everything worked fine + if (count($public_key) === count($encrypted_keys)) { + $result = strlen($sealed_data); + } + } + } + } + } else { + throw new EncryptionFailedException('Unsupported cipher '.$cipher_algo); + } + + return $result; + } } |