Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 108 additions & 9 deletions apps/encryption/lib/Crypto/Crypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
* @author Björn Schießle <[email protected]>
* @author Christoph Wurst <[email protected]>
* @author Clark Tomlinson <[email protected]>
* @author Côme Chilliet <[email protected]>
* @author Joas Schilling <[email protected]>
* @author Kevin Niehage <[email protected]>
* @author Lukas Reschke <[email protected]>
* @author Morris Jobke <[email protected]>
* @author Roeland Jago Douma <[email protected]>
Expand Down Expand Up @@ -40,6 +42,7 @@
use OCP\IL10N;
use OCP\ILogger;
use OCP\IUserSession;
use phpseclib\Crypt\RC4;

/**
* Class Crypt provides the encryption implementation of the default Nextcloud
Expand Down Expand Up @@ -517,12 +520,9 @@ public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $ciph
/**
* 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);
Expand Down Expand Up @@ -695,9 +695,9 @@ public function generateFileKey() {
}

/**
* @param $encKeyFile
* @param $shareKey
* @param $privateKey
* @param string $encKeyFile
* @param string $shareKey
* @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $privateKey
* @return string
* @throws MultiKeyDecryptException
*/
Expand All @@ -706,7 +706,8 @@ public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) {
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());
Expand All @@ -731,7 +732,7 @@ public function multiKeyEncrypt($plainContent, array $keyFiles) {
$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
Expand All @@ -749,7 +750,105 @@ public function multiKeyEncrypt($plainContent, array $keyFiles) {
}
}

/**
* 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase please ;)

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