mirror of
https://github.com/nextcloud/server.git
synced 2026-04-21 14:23:17 -04:00
Getting rid of openssl_seal and rc4 in server side encryption
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
This commit is contained in:
parent
c7c1133c15
commit
fbe282caeb
3 changed files with 94 additions and 24 deletions
|
|
@ -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');
|
||||
}
|
||||
|
|
@ -714,13 +726,54 @@ 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)) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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 '';
|
||||
|
|
|
|||
Loading…
Reference in a new issue