fall back to the ownCloud default encryption module and aes128 if we read a encrypted file without a header
This commit is contained in:
parent
e58029f8ad
commit
27683f9442
8 changed files with 88 additions and 20 deletions
|
@ -209,6 +209,15 @@ class Crypt {
|
|||
return $cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* get legacy cipher
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLegacyCipher() {
|
||||
return self::LEGACY_CIPHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $encryptedContent
|
||||
* @param string $iv
|
||||
|
|
|
@ -101,6 +101,7 @@ class Encryption implements IEncryptionModule {
|
|||
*
|
||||
* @param string $path to the file
|
||||
* @param string $user who read/write the file
|
||||
* @param string $mode php stream open mode
|
||||
* @param array $header contains the header data read from the file
|
||||
* @param array $accessList who has access to the file contains the key 'users' and 'public'
|
||||
*
|
||||
|
@ -108,12 +109,19 @@ class Encryption implements IEncryptionModule {
|
|||
* written to the header, in case of a write operation
|
||||
* or if no additional data is needed return a empty array
|
||||
*/
|
||||
public function begin($path, $user, array $header, array $accessList) {
|
||||
public function begin($path, $user, $mode, array $header, array $accessList) {
|
||||
|
||||
if (isset($header['cipher'])) {
|
||||
$this->cipher = $header['cipher'];
|
||||
} else {
|
||||
} else if (
|
||||
$mode === 'w'
|
||||
|| $mode === 'w+'
|
||||
|| $mode === 'wb'
|
||||
|| $mode === 'wb+'
|
||||
) {
|
||||
$this->cipher = $this->crypt->getCipher();
|
||||
} else {
|
||||
$this->cipher = $this->crypt->getLegacyCipher();
|
||||
}
|
||||
|
||||
$this->path = $this->getPathToRealFile($path);
|
||||
|
@ -234,7 +242,7 @@ class Encryption implements IEncryptionModule {
|
|||
public function decrypt($data) {
|
||||
$result = '';
|
||||
if (!empty($data)) {
|
||||
$result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey);
|
||||
$result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
|
@ -72,5 +72,32 @@ class EncryptionTest extends TestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestBegin
|
||||
*/
|
||||
public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $expected) {
|
||||
|
||||
$this->cryptMock->expects($this->any())
|
||||
->method('getCipher')
|
||||
->willReturn($defaultCipher);
|
||||
$this->cryptMock->expects($this->any())
|
||||
->method('getLegacyCipher')
|
||||
->willReturn($legacyCipher);
|
||||
|
||||
$result = $this->instance->begin('/user/files/foo.txt', 'user', $mode, $header, []);
|
||||
|
||||
$this->assertArrayHasKey('cipher', $result);
|
||||
$this->assertSame($expected, $result['cipher']);
|
||||
}
|
||||
|
||||
public function dataTestBegin() {
|
||||
return array(
|
||||
array('w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'myCipher'),
|
||||
array('r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'myCipher'),
|
||||
array('w', [], 'legacyCipher', 'defaultCipher', 'defaultCipher'),
|
||||
array('r', [], 'legacyCipher', 'defaultCipher', 'legacyCipher'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -53,6 +53,7 @@ class DummyModule implements IEncryptionModule {
|
|||
*
|
||||
* @param string $path to the file
|
||||
* @param string $user who read/write the file (null for public access)
|
||||
* @param string $mode php stream open mode
|
||||
* @param array $header contains the header data read from the file
|
||||
* @param array $accessList who has access to the file contains the key 'users' and 'public'
|
||||
*
|
||||
|
@ -60,7 +61,7 @@ class DummyModule implements IEncryptionModule {
|
|||
* written to the header, in case of a write operation
|
||||
* or if no additional data is needed return a empty array
|
||||
*/
|
||||
public function begin($path, $user, array $header, array $accessList) {
|
||||
public function begin($path, $user, $mode, array $header, array $accessList) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
|
|
@ -162,8 +162,9 @@ class Encryption extends Wrapper {
|
|||
public function file_get_contents($path) {
|
||||
|
||||
$encryptionModule = $this->getEncryptionModule($path);
|
||||
$info = $this->getCache()->get($path);
|
||||
|
||||
if ($encryptionModule) {
|
||||
if ($encryptionModule || $info['encrypted'] === true) {
|
||||
$handle = $this->fopen($path, "r");
|
||||
if (!$handle) {
|
||||
return false;
|
||||
|
@ -283,7 +284,8 @@ class Encryption extends Wrapper {
|
|||
$encryptionEnabled = $this->encryptionManager->isEnabled();
|
||||
$shouldEncrypt = false;
|
||||
$encryptionModule = null;
|
||||
$header = $this->getHeader($path);
|
||||
$rawHeader = $this->getHeader($path);
|
||||
$header = $this->util->readHeader($rawHeader);
|
||||
$fullPath = $this->getFullPath($path);
|
||||
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
|
||||
|
||||
|
@ -319,10 +321,18 @@ class Encryption extends Wrapper {
|
|||
$shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath);
|
||||
}
|
||||
} else {
|
||||
$info = $this->getCache()->get($path);
|
||||
// only get encryption module if we found one in the header
|
||||
// or if file should be encrypted according to the file cache
|
||||
if (!empty($encryptionModuleId)) {
|
||||
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId);
|
||||
$shouldEncrypt = true;
|
||||
} else if(empty($encryptionModuleId) && $info['encrypted'] === true) {
|
||||
// we come from a old installation. No header and/or no module defined
|
||||
// but the file is encrypted. In this case we need to use the
|
||||
// OC_DEFAULT_MODULE to read the file
|
||||
$encryptionModule = $this->encryptionManager->getEncryptionModule('OC_DEFAULT_MODULE');
|
||||
$shouldEncrypt = true;
|
||||
}
|
||||
}
|
||||
} catch (ModuleDoesNotExistsException $e) {
|
||||
|
@ -341,7 +351,7 @@ class Encryption extends Wrapper {
|
|||
$source = $this->storage->fopen($path, $mode);
|
||||
$handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header,
|
||||
$this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode,
|
||||
$size, $unencryptedSize);
|
||||
$size, $unencryptedSize, strlen($rawHeader));
|
||||
return $handle;
|
||||
} else {
|
||||
return $this->storage->fopen($path, $mode);
|
||||
|
@ -419,10 +429,13 @@ class Encryption extends Wrapper {
|
|||
$header = '';
|
||||
if ($this->storage->file_exists($path)) {
|
||||
$handle = $this->storage->fopen($path, 'r');
|
||||
$header = fread($handle, $this->util->getHeaderSize());
|
||||
$firstBlock = fread($handle, $this->util->getHeaderSize());
|
||||
fclose($handle);
|
||||
if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
|
||||
$header = $firstBlock;
|
||||
}
|
||||
}
|
||||
return $this->util->readHeader($header);
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -435,7 +448,8 @@ class Encryption extends Wrapper {
|
|||
*/
|
||||
protected function getEncryptionModule($path) {
|
||||
$encryptionModule = null;
|
||||
$header = $this->getHeader($path);
|
||||
$rawHeader = $this->getHeader($path);
|
||||
$header = $this->util->readHeader($rawHeader);
|
||||
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
|
||||
if (!empty($encryptionModuleId)) {
|
||||
try {
|
||||
|
|
|
@ -58,6 +58,9 @@ class Encryption extends Wrapper {
|
|||
/** @var integer */
|
||||
protected $unencryptedSize;
|
||||
|
||||
/** @var integer */
|
||||
protected $headerSize;
|
||||
|
||||
/** @var integer */
|
||||
protected $unencryptedBlockSize;
|
||||
|
||||
|
@ -104,7 +107,8 @@ class Encryption extends Wrapper {
|
|||
'util',
|
||||
'size',
|
||||
'unencryptedSize',
|
||||
'encryptionStorage'
|
||||
'encryptionStorage',
|
||||
'headerSize'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -125,6 +129,7 @@ class Encryption extends Wrapper {
|
|||
* @param string $mode
|
||||
* @param int $size
|
||||
* @param int $unencryptedSize
|
||||
* @param int $headerSize
|
||||
* @return resource
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
|
@ -138,7 +143,8 @@ class Encryption extends Wrapper {
|
|||
\OC\Encryption\File $file,
|
||||
$mode,
|
||||
$size,
|
||||
$unencryptedSize) {
|
||||
$unencryptedSize,
|
||||
$headerSize) {
|
||||
|
||||
$context = stream_context_create(array(
|
||||
'ocencryption' => array(
|
||||
|
@ -153,7 +159,8 @@ class Encryption extends Wrapper {
|
|||
'file' => $file,
|
||||
'size' => $size,
|
||||
'unencryptedSize' => $unencryptedSize,
|
||||
'encryptionStorage' => $encStorage
|
||||
'encryptionStorage' => $encStorage,
|
||||
'headerSize' => $headerSize
|
||||
)
|
||||
));
|
||||
|
||||
|
@ -235,7 +242,7 @@ class Encryption extends Wrapper {
|
|||
}
|
||||
|
||||
$accessList = $this->file->getAccessList($sharePath);
|
||||
$this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $this->header, $accessList);
|
||||
$this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $mode, $this->header, $accessList);
|
||||
|
||||
if (
|
||||
$mode === 'w'
|
||||
|
@ -246,7 +253,8 @@ class Encryption extends Wrapper {
|
|||
// We're writing a new file so start write counter with 0 bytes
|
||||
$this->unencryptedSize = 0;
|
||||
$this->writeHeader();
|
||||
$this->size = $this->util->getHeaderSize();
|
||||
$this->headerSize = $this->util->getHeaderSize();
|
||||
$this->size = $this->headerSize;
|
||||
} else {
|
||||
$this->skipHeader();
|
||||
}
|
||||
|
@ -300,7 +308,7 @@ class Encryption extends Wrapper {
|
|||
// 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
|
||||
$positionInFile = (int)floor($this->position / $this->unencryptedBlockSize) *
|
||||
$this->util->getBlockSize() + $this->util->getHeaderSize();
|
||||
$this->util->getBlockSize() + $this->headerSize;
|
||||
$resultFseek = parent::stream_seek($positionInFile);
|
||||
|
||||
// only allow writes on seekable streams, or at the end of the encrypted stream
|
||||
|
@ -367,7 +375,7 @@ class Encryption extends Wrapper {
|
|||
}
|
||||
|
||||
$newFilePosition = floor($newPosition / $this->unencryptedBlockSize)
|
||||
* $this->util->getBlockSize() + $this->util->getHeaderSize();
|
||||
* $this->util->getBlockSize() + $this->headerSize;
|
||||
|
||||
$oldFilePosition = parent::stream_tell();
|
||||
if (parent::stream_seek($newFilePosition)) {
|
||||
|
@ -440,7 +448,7 @@ class Encryption extends Wrapper {
|
|||
* read first block to skip the header
|
||||
*/
|
||||
protected function skipHeader() {
|
||||
parent::stream_read($this->util->getHeaderSize());
|
||||
parent::stream_read($this->headerSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ interface IEncryptionModule {
|
|||
*
|
||||
* @param string $path to the file
|
||||
* @param string $user who read/write the file (null for public access)
|
||||
* @param string $mode php stream open mode
|
||||
* @param array $header contains the header data read from the file
|
||||
* @param array $accessList who has access to the file contains the key 'users' and 'public'
|
||||
*
|
||||
|
@ -58,7 +59,7 @@ interface IEncryptionModule {
|
|||
* or if no additional data is needed return a empty array
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function begin($path, $user, array $header, array $accessList);
|
||||
public function begin($path, $user, $mode, array $header, array $accessList);
|
||||
|
||||
/**
|
||||
* last chunk received. This is the place where you can perform some final
|
||||
|
|
|
@ -44,7 +44,7 @@ class Encryption extends \Test\TestCase {
|
|||
|
||||
return \OC\Files\Stream\Encryption::wrap($source, $internalPath,
|
||||
$fullPath, $header, $uid, $encryptionModule, $storage, $encStorage,
|
||||
$util, $file, $mode, $size, $unencryptedSize);
|
||||
$util, $file, $mode, $size, $unencryptedSize, 8192);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue