diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php index 69d8757eb8..6c3aee47a5 100644 --- a/apps/encryption/lib/crypto/crypt.php +++ b/apps/encryption/lib/crypto/crypt.php @@ -169,10 +169,11 @@ class Crypt { /** * @param string $plainContent * @param string $passPhrase + * @param int $version * @return false|string * @throws EncryptionFailedException */ - public function symmetricEncryptFileContent($plainContent, $passPhrase) { + public function symmetricEncryptFileContent($plainContent, $passPhrase, $version) { if (!$plainContent) { $this->logger->error('Encryption Library, symmetrical encryption failed no content given', @@ -187,7 +188,8 @@ class Crypt { $passPhrase, $this->getCipher()); - $sig = $this->createSignature($encryptedContent, $passPhrase); + // Create a signature based on the key as well as the current version + $sig = $this->createSignature($encryptedContent, $passPhrase.$version); // combine content to encrypt the IV identifier and actual IV $catFile = $this->concatIV($encryptedContent, $iv); @@ -365,7 +367,8 @@ class Crypt { $hash = $this->generatePasswordHash($password, $cipher, $uid); $encryptedKey = $this->symmetricEncryptFileContent( $privateKey, - $hash + $hash, + 0 ); return $encryptedKey; @@ -404,9 +407,12 @@ class Crypt { self::HEADER_END) + strlen(self::HEADER_END)); } - $plainKey = $this->symmetricDecryptFileContent($privateKey, + $plainKey = $this->symmetricDecryptFileContent( + $privateKey, $password, - $cipher); + $cipher, + 0 + ); if ($this->isValidPrivateKey($plainKey) === false) { return false; @@ -437,15 +443,15 @@ class Crypt { * @param string $keyFileContents * @param string $passPhrase * @param string $cipher + * @param int $version * @return string * @throws DecryptionFailedException */ - public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $cipher = self::DEFAULT_CIPHER) { - + public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $cipher = self::DEFAULT_CIPHER, $version = 0) { $catFile = $this->splitMetaData($keyFileContents, $cipher); if ($catFile['signature'] !== false) { - $this->checkSignature($catFile['encrypted'], $passPhrase, $catFile['signature']); + $this->checkSignature($catFile['encrypted'], $passPhrase.$version, $catFile['signature']); } return $this->decrypt($catFile['encrypted'], diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index dc60c09478..90c60b8e0d 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -100,6 +100,9 @@ class Encryption implements IEncryptionModule { /** @var int unencrypted block size */ private $unencryptedBlockSize = 6126; + /** @var int Current version of the file */ + private $version = 0; + /** * @@ -163,7 +166,6 @@ class Encryption implements IEncryptionModule { * or if no additional data is needed return a empty array */ public function begin($path, $user, $mode, array $header, array $accessList) { - $this->path = $this->getPathToRealFile($path); $this->accessList = $accessList; $this->user = $user; @@ -180,6 +182,8 @@ class Encryption implements IEncryptionModule { $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user); } + $this->version = (int)$this->keyManager->getVersion($this->path); + if ( $mode === 'w' || $mode === 'w+' @@ -220,8 +224,13 @@ class Encryption implements IEncryptionModule { public function end($path) { $result = ''; if ($this->isWriteOperation) { + // Partial files do not increase the version + if(\OC\Files\Cache\Scanner::isPartialFile($path)) { + $this->version = $this->version-1; + } + $this->keyManager->setVersion($this->path, $this->version+1); if (!empty($this->writeCache)) { - $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey); + $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey, $this->version+1); $this->writeCache = ''; } $publicKeys = array(); @@ -258,7 +267,6 @@ class Encryption implements IEncryptionModule { * @return string encrypted data */ public function encrypt($data) { - // If extra data is left over from the last round, make sure it // is integrated into the next block if ($this->writeCache) { @@ -302,7 +310,11 @@ class Encryption implements IEncryptionModule { // Read the chunk from the start of $data $chunk = substr($data, 0, $this->unencryptedBlockSizeSigned); - $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey); + // Partial files do not increase the version + if(\OC\Files\Cache\Scanner::isPartialFile($this->path)) { + $this->version = $this->version - 1; + } + $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version+1); // Remove the chunk we just processed from // $data, leaving only unprocessed data in $data @@ -334,7 +346,7 @@ class Encryption implements IEncryptionModule { $result = ''; if (!empty($data)) { - $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher); + $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher, $this->version); } return $result; } @@ -349,6 +361,7 @@ class Encryption implements IEncryptionModule { */ public function update($path, $uid, array $accessList) { $fileKey = $this->keyManager->getFileKey($path, $uid); + $version = $this->keyManager->getVersion($path); if (!empty($fileKey)) { @@ -369,6 +382,8 @@ class Encryption implements IEncryptionModule { $this->keyManager->setAllFileKeys($path, $encryptedFileKey); + $this->keyManager->setVersion($path, $version); + } else { $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted', array('file' => $path, 'app' => 'encryption')); diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index b6365cf2cc..4cbb377a43 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -412,6 +412,24 @@ class KeyManager { return ''; } + /** + * Get the current version of a file + * + * @param string $path + * @return mixed + */ + public function getVersion($path) { + return $this->keyStorage->getFileKey($path, 'version', Encryption::ID); + } + + /** + * @param string $path + * @param string $version + */ + public function setVersion($path, $version) { + $this->keyStorage->setFileKey($path, 'version', $version, Encryption::ID); + } + /** * get the encrypted file key * @@ -546,6 +564,7 @@ class KeyManager { /** * @param string $path + * @return bool */ public function deleteAllFileKeys($path) { return $this->keyStorage->deleteAllFileKeys($path);