summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorSimon L <szaimen@e.mail.de>2023-03-02 17:15:45 +0100
committerGitHub <noreply@github.com>2023-03-02 17:15:45 +0100
commit0df20f6ebecef17a97f0b8afb6f5fc5b99e915a6 (patch)
tree2fa862c135089a02ccf89bdaf75b89701fc2122a /apps
parent6466c8ee31f3b30b8710c91c05d4b88d1d7ef086 (diff)
parentf2912ce8bcd4316b587e249d3deb94c461caddaf (diff)
downloadnextcloud-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.php117
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;
+ }
}