Merge pull request #36173 from nextcloud/enh/openssl-seal-custom

Use a PHP implementation of openssl_seal that allows to use modern ciphers
pull/37021/head
Simon L 1 year ago committed by GitHub
commit 0df20f6ebe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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;
}
}

Loading…
Cancel
Save