diff options
Diffstat (limited to 'lib/private/files/stream/encryption.php')
-rw-r--r-- | lib/private/files/stream/encryption.php | 126 |
1 files changed, 87 insertions, 39 deletions
diff --git a/lib/private/files/stream/encryption.php b/lib/private/files/stream/encryption.php index ddef9067bad..b4e06c99943 100644 --- a/lib/private/files/stream/encryption.php +++ b/lib/private/files/stream/encryption.php @@ -31,6 +31,9 @@ class Encryption extends Wrapper { /** @var \OC\Encryption\Util */ protected $util; + /** @var \OC\Encryption\File */ + protected $file; + /** @var \OCP\Encryption\IEncryptionModule */ protected $encryptionModule; @@ -43,6 +46,9 @@ class Encryption extends Wrapper { /** @var string */ protected $internalPath; + /** @var string */ + protected $cache; + /** @var integer */ protected $size; @@ -72,13 +78,16 @@ class Encryption extends Wrapper { /** * user who perform the read/write operation null for public access * - * @var string + * @var string */ protected $uid; /** @var bool */ protected $readOnly; + /** @var bool */ + protected $writeFlag; + /** @var array */ protected $expectedContextProperties; @@ -91,6 +100,7 @@ class Encryption extends Wrapper { 'encryptionModule', 'header', 'uid', + 'file', 'util', 'size', 'unencryptedSize', @@ -106,11 +116,12 @@ class Encryption extends Wrapper { * @param string $internalPath relative to mount point * @param string $fullPath relative to data/ * @param array $header - * @param sting $uid + * @param string $uid * @param \OCP\Encryption\IEncryptionModule $encryptionModule * @param \OC\Files\Storage\Storage $storage - * @param OC\Files\Storage\Wrapper\Encryption $encStorage + * @param \OC\Files\Storage\Wrapper\Encryption $encStorage * @param \OC\Encryption\Util $util + * @param \OC\Encryption\File $file * @param string $mode * @param int $size * @param int $unencryptedSize @@ -119,9 +130,15 @@ class Encryption extends Wrapper { * @throws \BadMethodCallException */ public static function wrap($source, $internalPath, $fullPath, array $header, - $uid, \OCP\Encryption\IEncryptionModule $encryptionModule, - \OC\Files\Storage\Storage $storage, \OC\Files\Storage\Wrapper\Encryption $encStorage, - \OC\Encryption\Util $util, $mode, $size, $unencryptedSize) { + $uid, + \OCP\Encryption\IEncryptionModule $encryptionModule, + \OC\Files\Storage\Storage $storage, + \OC\Files\Storage\Wrapper\Encryption $encStorage, + \OC\Encryption\Util $util, + \OC\Encryption\File $file, + $mode, + $size, + $unencryptedSize) { $context = stream_context_create(array( 'ocencryption' => array( @@ -133,6 +150,7 @@ class Encryption extends Wrapper { 'header' => $header, 'uid' => $uid, 'util' => $util, + 'file' => $file, 'size' => $size, 'unencryptedSize' => $unencryptedSize, 'encryptionStorage' => $encStorage @@ -180,7 +198,7 @@ class Encryption extends Wrapper { $context = parent::loadContext($name); foreach ($this->expectedContextProperties as $property) { - if (isset($context[$property])) { + if (array_key_exists($property, $context)) { $this->{$property} = $context[$property]; } else { throw new \BadMethodCallException('Invalid context, "' . $property . '" options not set'); @@ -194,6 +212,8 @@ class Encryption extends Wrapper { $this->loadContext('ocencryption'); $this->position = 0; + $this->cache = ''; + $this->writeFlag = false; $this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize(); if ( @@ -216,7 +236,7 @@ class Encryption extends Wrapper { $sharePath = dirname($path); } - $accessList = $this->util->getSharingUsersArray($sharePath); + $accessList = $this->file->getAccessList($sharePath); $this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $this->header, $accessList); return true; @@ -228,25 +248,26 @@ class Encryption extends Wrapper { $result = ''; // skip the header if we read the file from the beginning - if ($this->position === 0 && !empty($this->header)) { - parent::stream_read($this->util->getBlockSize()); + if ($this->position === 0) { + parent::stream_read($this->util->getHeaderSize()); } +// $count = min($count, $this->unencryptedSize - $this->position); while ($count > 0) { $remainingLength = $count; // update the cache of the current block - $data = parent::stream_read($this->util->getBlockSize()); - $decrypted = $this->encryptionModule->decrypt($data); + $this->readCache(); // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); // if entire read inside current block then only position needs to be updated if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { - $result .= substr($decrypted, $blockPosition, $remainingLength); + $result .= substr($this->cache, $blockPosition, $remainingLength); $this->position += $remainingLength; $count = 0; - // otherwise remainder of current block is fetched, the block is flushed and the position updated + // otherwise remainder of current block is fetched, the block is flushed and the position updated } else { - $result .= substr($decrypted, $blockPosition); + $result .= substr($this->cache, $blockPosition); + $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); $count -= ($this->unencryptedBlockSize - $blockPosition); } @@ -259,6 +280,7 @@ class Encryption extends Wrapper { if ($this->position === 0) { $this->writeHeader(); + $this->size+=$this->util->getHeaderSize(); } $length = 0; @@ -266,9 +288,8 @@ class Encryption extends Wrapper { while (strlen($data) > 0) { $remainingLength = strlen($data); - // read current block - $currentBlock = parent::stream_read($this->util->getBlockSize()); - $decrypted = $this->encryptionModule->decrypt($currentBlock, $this->uid); + // set the cache to the current 6126 block + $this->readCache(); // for seekable streams the pointer is moved back to the beginning of the encrypted block // flush will start writing there when the position moves to another block @@ -277,7 +298,10 @@ class Encryption extends Wrapper { $resultFseek = parent::stream_seek($positionInFile); // only allow writes on seekable streams, or at the end of the encrypted stream - if ($resultFseek || $positionInFile === $this->size) { + if (!($this->readOnly) && ($resultFseek || $positionInFile === $this->size)) { + + // switch the writeFlag so flush() will write the block + $this->writeFlag = true; // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); @@ -285,28 +309,22 @@ class Encryption extends Wrapper { // if so, overwrite existing data (if any) // update position and liberate $data if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { - $decrypted = substr($decrypted, 0, $blockPosition) - . $data . substr($decrypted, $blockPosition + $remainingLength); - $encrypted = $this->encryptionModule->encrypt($decrypted); - parent::stream_write($encrypted); + $this->cache = substr($this->cache, 0, $blockPosition) + . $data . substr($this->cache, $blockPosition + $remainingLength); $this->position += $remainingLength; $length += $remainingLength; $data = ''; - // if $data doens't fit the current block, the fill the current block and reiterate - // after the block is filled, it is flushed and $data is updatedxxx + // if $data doesn't fit the current block, the fill the current block and reiterate + // after the block is filled, it is flushed and $data is updatedxxx } else { - $decrypted = substr($decrypted, 0, $blockPosition) . + $this->cache = substr($this->cache, 0, $blockPosition) . substr($data, 0, $this->unencryptedBlockSize - $blockPosition); - $encrypted = $this->encryptionModule->encrypt($decrypted); - parent::stream_write($encrypted); + $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); - $this->size = max($this->size, $this->stream_tell()); $length += ($this->unencryptedBlockSize - $blockPosition); $data = substr($data, $this->unencryptedBlockSize - $blockPosition); } } else { - $encrypted = $this->encryptionModule->encrypt($data); - parent::stream_write($encrypted); $data = ''; } } @@ -345,7 +363,11 @@ class Encryption extends Wrapper { $newFilePosition = floor($newPosition / $this->unencryptedBlockSize) * $this->util->getBlockSize() + $this->util->getHeaderSize(); + $oldFilePosition = parent::stream_tell(); if (parent::stream_seek($newFilePosition)) { + parent::stream_seek($oldFilePosition); + $this->flush(); + parent::stream_seek($newFilePosition); $this->position = $newPosition; $return = true; } @@ -355,13 +377,6 @@ class Encryption extends Wrapper { public function stream_close() { $this->flush(); - return parent::stream_close(); - } - - /** - * tell encryption module that we are done and write remaining data to the file - */ - protected function flush() { $remainingData = $this->encryptionModule->end($this->fullPath); if ($this->readOnly === false) { if(!empty($remainingData)) { @@ -369,17 +384,50 @@ class Encryption extends Wrapper { } $this->encryptionStorage->updateUnencryptedSize($this->fullPath, $this->unencryptedSize); } + return parent::stream_close(); } + /** + * write block to file + */ + protected function flush() { + // write to disk only when writeFlag was set to 1 + if ($this->writeFlag) { + // Disable the file proxies so that encryption is not + // automatically attempted when the file is written to disk - + // we are handling that separately here and we don't want to + // get into an infinite loop + $encrypted = $this->encryptionModule->encrypt($this->cache); + parent::stream_write($encrypted); + $this->writeFlag = false; + $this->size = max($this->size, parent::stream_tell()); + } + // always empty the cache (otherwise readCache() will not fill it with the new block) + $this->cache = ''; + } + + /** + * read block to file + */ + protected function readCache() { + // cache should always be empty string when this function is called + // don't try to fill the cache when trying to write at the end of the unencrypted file when it coincides with new block + if ($this->cache === '' && !($this->position === $this->unencryptedSize && ($this->position % $this->unencryptedBlockSize) === 0)) { + // Get the data from the file handle + $data = parent::stream_read($this->util->getBlockSize()); + $this->cache = $this->encryptionModule->decrypt($data); + } + } /** * write header at beginning of encrypted file * + * @return integer * @throws EncryptionHeaderKeyExistsException if header key is already in use */ private function writeHeader() { $header = $this->util->createHeader($this->newHeader, $this->encryptionModule); - parent::stream_write($header); + return parent::stream_write($header); } } |