2012-08-16 22:18:18 +04:00
< ? php
/**
* ownCloud
*
* @ author Robin Appelman
* @ copyright 2011 Robin Appelman icewind1991 @ gmail . com
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation ; either
* version 3 of the License , or any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details .
*
* You should have received a copy of the GNU Affero General Public
* License along with this library . If not , see < http :// www . gnu . org / licenses />.
*
*/
/**
* transparently encrypted filestream
*
* you can use it as wrapper around an existing stream by setting CryptStream :: $sourceStreams [ 'foo' ] = array ( 'path' => $path , 'stream' => $stream )
* and then fopen ( 'crypt://streams/foo' );
*/
namespace OCA_Encryption ;
class Stream {
public static $sourceStreams = array ();
2012-09-11 16:40:45 +04:00
# TODO: make all below properties private again once unit testing is configured correctly
public $rawPath ; // The raw path received by stream_open
private $handle ; // Resource returned by fopen
2012-08-16 22:18:18 +04:00
private $path ;
private $readBuffer ; // For streams that dont support seeking
private $meta = array (); // Header / meta for source stream
private $count ;
private $writeCache ;
2012-09-11 16:40:45 +04:00
public $size ;
2012-08-23 19:43:10 +04:00
private $keyfile ;
2012-08-16 22:18:18 +04:00
private static $view ;
public function stream_open ( $path , $mode , $options , & $opened_path ) {
// Get access to filesystem via filesystemview object
if ( ! self :: $view ) {
self :: $view = new \OC_FilesystemView ( '' );
}
// Get the bare file path
$path = str_replace ( 'crypt://' , '' , $path );
2012-08-23 19:43:10 +04:00
$this -> rawPath = $path ;
2012-08-16 22:18:18 +04:00
if (
dirname ( $path ) == 'streams'
and isset ( self :: $sourceStreams [ basename ( $path )] )
) {
2012-09-11 16:40:45 +04:00
// Is this just for unit testing purposes?
2012-08-16 22:18:18 +04:00
2012-09-11 16:40:45 +04:00
$this -> handle = self :: $sourceStreams [ basename ( $path )][ 'stream' ];
2012-08-16 22:18:18 +04:00
$this -> path = self :: $sourceStreams [ basename ( $path )][ 'path' ];
$this -> size = self :: $sourceStreams [ basename ( $path )][ 'size' ];
} else {
if (
$mode == 'w'
or $mode == 'w+'
or $mode == 'wb'
or $mode == 'wb+'
) {
$this -> size = 0 ;
} else {
2012-09-11 16:40:45 +04:00
//$this->size = self::$view->filesize( $path, $mode );
2012-08-16 22:18:18 +04:00
2012-09-11 16:40:45 +04:00
$this -> size = filesize ( $path );
2012-08-16 22:18:18 +04:00
}
// Disable fileproxies so we can open the source file without recursive encryption
\OC_FileProxy :: $enabled = false ;
2012-09-11 16:40:45 +04:00
$this -> handle = fopen ( $path , $mode );
//$this->handle = self::$view->fopen( $path, $mode );
2012-08-16 22:18:18 +04:00
\OC_FileProxy :: $enabled = true ;
2012-09-11 16:40:45 +04:00
if ( ! is_resource ( $this -> handle ) ) {
2012-08-16 22:18:18 +04:00
\OCP\Util :: writeLog ( 'files_encryption' , 'failed to open ' . $path , OCP\Util :: ERROR );
}
}
2012-09-11 16:40:45 +04:00
if ( is_resource ( $this -> handle ) ) {
2012-08-16 22:18:18 +04:00
2012-09-11 16:40:45 +04:00
$this -> meta = stream_get_meta_data ( $this -> handle );
2012-08-16 22:18:18 +04:00
}
2012-09-11 16:40:45 +04:00
return is_resource ( $this -> handle );
2012-08-16 22:18:18 +04:00
}
2012-09-11 16:40:45 +04:00
public function stream_seek ( $offset , $whence = SEEK_SET ) {
2012-08-16 22:18:18 +04:00
$this -> flush ();
2012-09-11 16:40:45 +04:00
fseek ( $this -> handle , $offset , $whence );
2012-08-16 22:18:18 +04:00
}
public function stream_tell () {
2012-09-11 16:40:45 +04:00
return ftell ( $this -> handle );
2012-08-16 22:18:18 +04:00
}
2012-08-23 19:43:10 +04:00
public function stream_read ( $count ) {
2012-09-11 16:40:45 +04:00
2012-08-23 19:43:10 +04:00
$this -> writeCache = '' ;
if ( $count != 8192 ) {
// $count will always be 8192 https://bugs.php.net/bug.php?id=21641
// This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed'
\OCP\Util :: writeLog ( 'files_encryption' , 'PHP "bug" 21641 no longer holds, decryption system requires refactoring' , OCP\Util :: FATAL );
2012-08-16 22:18:18 +04:00
die ();
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
}
2012-08-23 19:43:10 +04:00
2012-09-11 16:40:45 +04:00
// $pos = ftell( $this->handle );
//
$data = fread ( $this -> handle , 8192 );
//echo "\n\nPRE DECRYPTION = $data\n\n";
//
// if ( strlen( $data ) ) {
$this -> getKey ();
echo " \n \n GROWL { $this -> keyfile } \n \n " ;
$key = file_get_contents ( '/home/samtuke/owncloud/git/oc3/data/admin/files_encryption/keyfiles/tmp-1346255589.key' );
2012-08-23 19:43:10 +04:00
$result = Crypt :: symmetricDecryptFileContent ( $data , $this -> keyfile );
2012-09-11 16:40:45 +04:00
echo " \n \n \n \n ----------------------------- \n \n NEWS " ;
echo " \n \n \$ data = $data " ;
echo " \n \n \$ key = $key " ;
echo " \n \n \$ result = $result " ;
echo " \n \n \n \n ----------------------------- \n \n " ;
//trigger_error("CAT $result");
2012-08-23 19:43:10 +04:00
2012-09-11 16:40:45 +04:00
// } else {
//
// $result = '';
//
// }
2012-08-23 19:43:10 +04:00
2012-09-11 16:40:45 +04:00
// $length = $this->size - $pos;
//
// if ( $length < 8192 ) {
//
// $result = substr( $result, 0, $length );
//
// }
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
return $result ;
2012-08-23 19:43:10 +04:00
}
/**
* @ brief Get the keyfile for the current file , generate one if necessary
2012-09-11 16:40:45 +04:00
* @ param bool $generate if true , a new key will be generated if none can be found
2012-08-23 19:43:10 +04:00
*/
2012-09-11 16:40:45 +04:00
public function getKey ( $generate = true ) {
2012-08-23 19:43:10 +04:00
# TODO: Move this user call out of here - it belongs elsewhere
$user = \OCP\User :: getUser ();
2012-09-11 16:40:45 +04:00
if ( self :: $view -> file_exists ( $this -> rawPath ) ) {
# TODO: add error handling for when file exists but no keyfile
2012-08-23 19:43:10 +04:00
// If the data is to be written to an existing file, fetch its keyfile
2012-09-11 16:40:45 +04:00
$this -> keyfile = Keymanager :: getFileKey ( $this -> rawPath );
2012-08-23 19:43:10 +04:00
} else {
2012-09-11 16:40:45 +04:00
if ( $generate ) {
// If the data is to be written to a new file, generate a new keyfile
$this -> keyfile = Crypt :: generateKey ();
}
2012-08-23 19:43:10 +04:00
}
2012-08-16 22:18:18 +04:00
}
/**
2012-09-11 16:40:45 +04:00
* @ brief Take plain data destined to be written , encrypt it , and write it block by block
2012-08-16 22:18:18 +04:00
*/
public function stream_write ( $data ) {
2012-08-23 19:43:10 +04:00
\OC_FileProxy :: $enabled = false ;
2012-08-16 22:18:18 +04:00
$length = strlen ( $data );
$written = 0 ;
2012-09-11 16:40:45 +04:00
$currentPos = ftell ( $this -> handle );
2012-08-16 22:18:18 +04:00
# TODO: Move this user call out of here - it belongs elsewhere
$user = \OCP\User :: getUser ();
2012-08-23 19:43:10 +04:00
// Set keyfile property for file in question
$this -> getKey ();
2012-08-16 22:18:18 +04:00
2012-08-23 19:43:10 +04:00
if ( ! self :: $view -> file_exists ( $this -> rawPath . $user ) ) {
2012-08-16 22:18:18 +04:00
2012-08-23 19:43:10 +04:00
// Save keyfile in parallel directory structure
Keymanager :: setFileKey ( $this -> rawPath , $this -> keyfile , new \OC_FilesystemView ( '/' ) );
2012-08-16 22:18:18 +04:00
}
2012-09-11 16:40:45 +04:00
// // If data exists in the writeCache
2012-08-23 22:19:39 +04:00
// if ( $this->writeCache ) {
2012-09-11 16:40:45 +04:00
//
// trigger_error("write cache is set");
//
// // Concat writeCache to start of $data
2012-08-23 22:19:39 +04:00
// $data = $this->writeCache . $data;
//
// $this->writeCache = '';
//
// }
2012-09-11 16:40:45 +04:00
//
2012-08-23 22:19:39 +04:00
// // Make sure we always start on a block start
// if ( 0 != ( $currentPos % 8192 ) ) { // If we're not at the end of file yet (in the final chunk), if there will be no bytes left to read after the current chunk
//
2012-09-11 16:40:45 +04:00
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
2012-08-23 22:19:39 +04:00
//
2012-09-11 16:40:45 +04:00
// $encryptedBlock = fread( $this->handle, 8192 );
2012-08-23 22:19:39 +04:00
//
2012-09-11 16:40:45 +04:00
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
2012-08-23 22:19:39 +04:00
//
// $block = Crypt::symmetricDecryptFileContent( $encryptedBlock, $this->keyfile );
//
// $x = substr( $block, 0, $currentPos % 8192 );
//
// $data = $x . $data;
//
2012-09-11 16:40:45 +04:00
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
2012-08-23 22:19:39 +04:00
//
// }
2012-09-11 16:40:45 +04:00
/*
$currentPos = ftell ( $this -> handle ); */
// // While there still remains somed data to be written
// while( strlen( $data ) > 0 ) {
//
// $remainingLength = strlen( $data );
//
// // If data remaining to be written is less than the size of 1 block
2012-08-23 22:19:39 +04:00
// if ( $remainingLength < 8192 ) {
2012-09-11 16:40:45 +04:00
//
// //trigger_error("remaining length < 8192");
//
// // Set writeCache to contents of $data
2012-08-23 22:19:39 +04:00
// $this->writeCache = $data;
//
// $data = '';
//
// } else {
2012-09-11 16:40:45 +04:00
$encrypted = Crypt :: symmetricEncryptFileContent ( $data , $this -> keyfile );
file_put_contents ( '/home/samtuke/tmp.txt' , $encrypted );
2012-08-23 19:43:10 +04:00
2012-09-11 16:40:45 +04:00
//echo "\n\nFRESHLY ENCRYPTED = $encrypted\n\n";
2012-08-16 22:18:18 +04:00
2012-09-11 16:40:45 +04:00
fwrite ( $this -> handle , $encrypted );
2012-08-16 22:18:18 +04:00
2012-08-23 22:19:39 +04:00
$data = substr ( $data , 8192 );
2012-08-16 22:18:18 +04:00
2012-08-23 22:19:39 +04:00
// }
//
// }
2012-08-16 22:18:18 +04:00
$this -> size = max ( $this -> size , $currentPos + $length );
return $length ;
}
public function stream_set_option ( $option , $arg1 , $arg2 ) {
switch ( $option ) {
case STREAM_OPTION_BLOCKING :
2012-09-11 16:40:45 +04:00
stream_set_blocking ( $this -> handle , $arg1 );
2012-08-16 22:18:18 +04:00
break ;
case STREAM_OPTION_READ_TIMEOUT :
2012-09-11 16:40:45 +04:00
stream_set_timeout ( $this -> handle , $arg1 , $arg2 );
2012-08-16 22:18:18 +04:00
break ;
case STREAM_OPTION_WRITE_BUFFER :
2012-09-11 16:40:45 +04:00
stream_set_write_buffer ( $this -> handle , $arg1 , $arg2 );
2012-08-16 22:18:18 +04:00
}
}
public function stream_stat () {
2012-09-11 16:40:45 +04:00
return fstat ( $this -> handle );
2012-08-16 22:18:18 +04:00
}
public function stream_lock ( $mode ) {
2012-09-11 16:40:45 +04:00
flock ( $this -> handle , $mode );
2012-08-16 22:18:18 +04:00
}
public function stream_flush () {
2012-09-11 16:40:45 +04:00
return fflush ( $this -> handle ); // Not a typo: http://php.net/manual/en/function.fflush.php
2012-08-16 22:18:18 +04:00
}
public function stream_eof () {
2012-09-11 16:40:45 +04:00
return feof ( $this -> handle );
2012-08-16 22:18:18 +04:00
}
private function flush () {
2012-09-11 16:40:45 +04:00
if ( $this -> writeCache ) {
// Set keyfile property for file in question
$this -> getKey ();
//echo "\n\nFLUSH = {$this->writeCache}\n\n";
$encrypted = Crypt :: symmetricBlockEncryptFileContent ( $this -> writeCache , $this -> keyfile );
//echo "\n\nENCFLUSH = $encrypted\n\n";
fwrite ( $this -> handle , $encrypted );
$this -> writeCache = '' ;
2012-08-16 22:18:18 +04:00
}
2012-09-11 16:40:45 +04:00
2012-08-16 22:18:18 +04:00
}
public function stream_close () {
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
$this -> flush ();
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
if ( $this -> meta [ 'mode' ] != 'r' and $this -> meta [ 'mode' ] != 'rb' ) {
2012-08-23 19:43:10 +04:00
\OC_FileCache :: put ( $this -> path , array ( 'encrypted' => true , 'size' => $this -> size ), '' );
2012-08-16 22:18:18 +04:00
}
2012-08-23 19:43:10 +04:00
2012-09-11 16:40:45 +04:00
return fclose ( $this -> handle );
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
}
2012-08-23 19:43:10 +04:00
2012-08-16 22:18:18 +04:00
}