Keep track of file version

This way it is not possible anymore for an external storage admin to put up old versions of the file.
This commit is contained in:
Lukas Reschke 2016-02-02 20:00:36 +01:00
parent d5c1596887
commit b5824f024a
3 changed files with 53 additions and 13 deletions

View File

@ -169,10 +169,11 @@ class Crypt {
/** /**
* @param string $plainContent * @param string $plainContent
* @param string $passPhrase * @param string $passPhrase
* @param int $version
* @return false|string * @return false|string
* @throws EncryptionFailedException * @throws EncryptionFailedException
*/ */
public function symmetricEncryptFileContent($plainContent, $passPhrase) { public function symmetricEncryptFileContent($plainContent, $passPhrase, $version) {
if (!$plainContent) { if (!$plainContent) {
$this->logger->error('Encryption Library, symmetrical encryption failed no content given', $this->logger->error('Encryption Library, symmetrical encryption failed no content given',
@ -187,7 +188,8 @@ class Crypt {
$passPhrase, $passPhrase,
$this->getCipher()); $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 // combine content to encrypt the IV identifier and actual IV
$catFile = $this->concatIV($encryptedContent, $iv); $catFile = $this->concatIV($encryptedContent, $iv);
@ -365,7 +367,8 @@ class Crypt {
$hash = $this->generatePasswordHash($password, $cipher, $uid); $hash = $this->generatePasswordHash($password, $cipher, $uid);
$encryptedKey = $this->symmetricEncryptFileContent( $encryptedKey = $this->symmetricEncryptFileContent(
$privateKey, $privateKey,
$hash $hash,
0
); );
return $encryptedKey; return $encryptedKey;
@ -404,9 +407,12 @@ class Crypt {
self::HEADER_END) + strlen(self::HEADER_END)); self::HEADER_END) + strlen(self::HEADER_END));
} }
$plainKey = $this->symmetricDecryptFileContent($privateKey, $plainKey = $this->symmetricDecryptFileContent(
$privateKey,
$password, $password,
$cipher); $cipher,
0
);
if ($this->isValidPrivateKey($plainKey) === false) { if ($this->isValidPrivateKey($plainKey) === false) {
return false; return false;
@ -437,15 +443,15 @@ class Crypt {
* @param string $keyFileContents * @param string $keyFileContents
* @param string $passPhrase * @param string $passPhrase
* @param string $cipher * @param string $cipher
* @param int $version
* @return string * @return string
* @throws DecryptionFailedException * @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); $catFile = $this->splitMetaData($keyFileContents, $cipher);
if ($catFile['signature'] !== false) { if ($catFile['signature'] !== false) {
$this->checkSignature($catFile['encrypted'], $passPhrase, $catFile['signature']); $this->checkSignature($catFile['encrypted'], $passPhrase.$version, $catFile['signature']);
} }
return $this->decrypt($catFile['encrypted'], return $this->decrypt($catFile['encrypted'],

View File

@ -100,6 +100,9 @@ class Encryption implements IEncryptionModule {
/** @var int unencrypted block size */ /** @var int unencrypted block size */
private $unencryptedBlockSize = 6126; 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 * or if no additional data is needed return a empty array
*/ */
public function begin($path, $user, $mode, array $header, array $accessList) { public function begin($path, $user, $mode, array $header, array $accessList) {
$this->path = $this->getPathToRealFile($path); $this->path = $this->getPathToRealFile($path);
$this->accessList = $accessList; $this->accessList = $accessList;
$this->user = $user; $this->user = $user;
@ -180,6 +182,8 @@ class Encryption implements IEncryptionModule {
$this->fileKey = $this->keyManager->getFileKey($this->path, $this->user); $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
} }
$this->version = (int)$this->keyManager->getVersion($this->path);
if ( if (
$mode === 'w' $mode === 'w'
|| $mode === 'w+' || $mode === 'w+'
@ -220,8 +224,13 @@ class Encryption implements IEncryptionModule {
public function end($path) { public function end($path) {
$result = ''; $result = '';
if ($this->isWriteOperation) { 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)) { 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 = ''; $this->writeCache = '';
} }
$publicKeys = array(); $publicKeys = array();
@ -258,7 +267,6 @@ class Encryption implements IEncryptionModule {
* @return string encrypted data * @return string encrypted data
*/ */
public function encrypt($data) { public function encrypt($data) {
// If extra data is left over from the last round, make sure it // If extra data is left over from the last round, make sure it
// is integrated into the next block // is integrated into the next block
if ($this->writeCache) { if ($this->writeCache) {
@ -302,7 +310,11 @@ class Encryption implements IEncryptionModule {
// Read the chunk from the start of $data // Read the chunk from the start of $data
$chunk = substr($data, 0, $this->unencryptedBlockSizeSigned); $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 // Remove the chunk we just processed from
// $data, leaving only unprocessed data in $data // $data, leaving only unprocessed data in $data
@ -334,7 +346,7 @@ class Encryption implements IEncryptionModule {
$result = ''; $result = '';
if (!empty($data)) { 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; return $result;
} }
@ -349,6 +361,7 @@ class Encryption implements IEncryptionModule {
*/ */
public function update($path, $uid, array $accessList) { public function update($path, $uid, array $accessList) {
$fileKey = $this->keyManager->getFileKey($path, $uid); $fileKey = $this->keyManager->getFileKey($path, $uid);
$version = $this->keyManager->getVersion($path);
if (!empty($fileKey)) { if (!empty($fileKey)) {
@ -369,6 +382,8 @@ class Encryption implements IEncryptionModule {
$this->keyManager->setAllFileKeys($path, $encryptedFileKey); $this->keyManager->setAllFileKeys($path, $encryptedFileKey);
$this->keyManager->setVersion($path, $version);
} else { } else {
$this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted', $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted',
array('file' => $path, 'app' => 'encryption')); array('file' => $path, 'app' => 'encryption'));

View File

@ -412,6 +412,24 @@ class KeyManager {
return ''; 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 * get the encrypted file key
* *
@ -546,6 +564,7 @@ class KeyManager {
/** /**
* @param string $path * @param string $path
* @return bool
*/ */
public function deleteAllFileKeys($path) { public function deleteAllFileKeys($path) {
return $this->keyStorage->deleteAllFileKeys($path); return $this->keyStorage->deleteAllFileKeys($path);