Implemented writing of keyfiles and directory hierarchy in proxy class

Added crypt::findFiles() method for finding different types of files, ready for batch encrypting / decrypting
Added comments to postFopen in proxy class
This commit is contained in:
Sam Tuke 2012-07-31 19:35:36 +01:00
parent f6863f9e51
commit 84fd62b130
6 changed files with 653 additions and 485 deletions

View File

@ -37,7 +37,7 @@ class Hooks {
public static function login( $params ) {
if (Crypt::mode($params['uid'])=='server') {
if ( Crypt::mode( $params['uid'] ) == 'server' ) {
$view = new \OC_FilesystemView( '/' );

View File

@ -35,13 +35,16 @@ class Crypt {
* @param string user name (use system wide setting if name=null)
* @return string 'client' or 'server'
*/
public static function mode($user=null) {
public static function mode( $user = null ) {
$mode = \OC_Appconfig::getValue('files_encryption', 'mode', 'unknown');
$mode = \OC_Appconfig::getValue( 'files_encryption', 'mode', 'unknown' );
if ( $mode == 'unknown' ) {
if ($mode == 'unknown') {
error_log('no encryption mode configured');
return false;
}
return $mode;

View File

@ -28,11 +28,11 @@
*/
class OC_CryptStream{
public static $sourceStreams=array();
public static $sourceStreams = array();
private $source;
private $path;
private $readBuffer;//for streams that dont support seeking
private $meta=array();//header/meta for source stream
private $readBuffer; // For streams that dont support seeking
private $meta = array(); // Header / meta for source stream
private $count;
private $writeCache;
private $size;
@ -98,38 +98,69 @@ class OC_CryptStream{
return $result;
}
public function stream_write($data){
$length=strlen($data);
$written=0;
$currentPos=ftell($this->source);
if($this->writeCache){
$data=$this->writeCache.$data;
$this->writeCache='';
public function stream_write( $data ){
$length = strlen( $data );
$written = 0;
$currentPos = ftell( $this->source );
if( $this->writeCache ){
$data = $this->writeCache.$data;
$this->writeCache = '';
}
if($currentPos%8192!=0){
if( $currentPos%8192 != 0 ){
//make sure we always start on a block start
fseek($this->source,-($currentPos%8192),SEEK_CUR);
$encryptedBlock=fread($this->source,8192);
fseek($this->source,-($currentPos%8192),SEEK_CUR);
$block=OC_Crypt::decrypt($encryptedBlock);
$data=substr($block,0,$currentPos%8192).$data;
fseek($this->source,-($currentPos%8192),SEEK_CUR);
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
$encryptedBlock = fread( $this->source,8192 );
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
$block = OC_Crypt::decrypt( $encryptedBlock );
$data = substr( $block,0,$currentPos%8192 ).$data;
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
}
$currentPos=ftell($this->source);
while($remainingLength=strlen($data)>0){
if($remainingLength<8192){
$this->writeCache=$data;
$data='';
$currentPos = ftell( $this->source );
while( $remainingLength = strlen( $data )>0 ){
if( $remainingLength<8192 ){
$this->writeCache = $data;
$data = '';
}else{
$encrypted=OC_Crypt::encrypt(substr($data,0,8192));
fwrite($this->source,$encrypted);
$data=substr($data,8192);
$encrypted = OC_Crypt::encrypt( substr( $data,0,8192 ) );
fwrite( $this->source,$encrypted );
$data = substr( $data,8192 );
}
}
$this->size=max($this->size,$currentPos+$length);
$this->size = max( $this->size,$currentPos+$length );
return $length;
}
public function stream_set_option($option,$arg1,$arg2){
switch($option){
case STREAM_OPTION_BLOCKING:

View File

@ -27,7 +27,7 @@ namespace OCA_Encryption;
*/
class Keymanager {
# TODO: Try and get rid of username dependencies as these methods need to be used in a proxy class that doesn't have username access
# TODO: make all dependencies explicit, such as ocfsview objects, by adding them as method arguments (dependency injection)
/**
* @brief retrieve private key from a user
@ -60,9 +60,9 @@ class Keymanager {
* @param string user name of the file owner
* @return string file key or false
*/
public static function getFileKey($userId, $path) {
public static function getFileKey( $userId, $path ) {
$keypath = ltrim($path, '/');
$keypath = ltrim( $path, '/' );
$user = $userId;
// update $keypath and $user if path point to a file shared by someone else
@ -132,24 +132,28 @@ class Keymanager {
\OC_FileProxy::$enabled = false;
$targetpath = ltrim($path, '/');
$targetpath = ltrim( $path, '/' );
$user = $userId;
// update $keytarget and $user if key belongs to a file shared by someone else
$query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );
$result = $query->execute( array ('/'.$userId.'/files/'.$targetpath, $userId));
if ($row = $result->fetchRow()){
$result = $query->execute( array ( '/'.$userId.'/files/'.$targetpath, $userId ) );
if ( $row = $result->fetchRow( ) ) {
$targetpath = $row['source'];
$targetpath_parts=explode('/',$targetpath);
$targetpath_parts=explode( '/',$targetpath );
$user = $targetpath_parts[1];
$targetpath = str_replace('/'.$user.'/files/', '', $targetpath);
$targetpath = str_replace( '/'.$user.'/files/', '', $targetpath );
//TODO: check for write permission on shared file once the new sharing API is in place
}
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption/keyfiles' );
$path_parts = pathinfo($targetpath);
if (!$view->file_exists($path_parts['dirname'])) $view->mkdir($path_parts['dirname']);
$path_parts = pathinfo( $targetpath );
if ( !$view->file_exists( $path_parts['dirname'] ) ) $view->mkdir( $path_parts['dirname'] );
$result = $view->file_put_contents( '/' . $targetpath . '.key', $key );
\OC_FileProxy::$enabled = true;

View File

@ -109,10 +109,14 @@ class Proxy extends \OC_FileProxy {
// Replace plain content with encrypted content by reference
$data = $encrypted['encrypted'];
# TODO: check if file is in subdirectories, and if so, create those parent directories. Or else monitor creation of directories using hooks to ensure path will always exist (what about existing directories when encryption is enabled?)
$filePath = explode( '/', $path );
// Save keyfile for newly encrypted file in parallel directory
Keymanager::setFileKey( \OCP\USER::getUser(), $path, $encrypted['key'] );
$filePath = array_slice( $filePath, 3 );
$filePath = '/' . implode( '/', $filePath );
// Save keyfile for newly encrypted file in parallel directory tree
Keymanager::setFileKey( \OCP\USER::getUser(), $filePath, $encrypted['key'] );
// Update the file cache with file info
\OC_FileCache::put( $path, array( 'encrypted'=>true, 'size' => $size ), '' );
@ -124,38 +128,80 @@ class Proxy extends \OC_FileProxy {
public function postFile_get_contents( $path, $data ) {
if ( Crypt::isEncryptedContent( $data ) ) {
trigger_error('best');
$filePath = explode( '/', $path );
$filePath = array_slice( $filePath, 3 );
$filePath = '/' . implode( '/', $filePath );
trigger_error( "CAT " . $filePath);
$cached = \OC_FileCache_Cached::get( $path, '' );
$data = Crypt::symmetricDecryptFileContent( $data, $_SESSION['enckey'] );
// Get keyfile for encrypted file
$keyFile = Keymanager::getFileKey( \OCP\USER::getUser(), $filePath );
$data = Crypt::symmetricDecryptFileContent( $data, $keyFile );
}
return $data;
}
public function postFopen($path,&$result){
public function postFopen( $path, &$result ){
if ( !$result ) {
if(!$result){
return $result;
}
$meta=stream_get_meta_data($result);
if(Crypt::isEncryptedContent($path)){
fclose($result);
$result=fopen('crypt://'.$path,$meta['mode']);
}elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb'){
if( \OC_Filesystem::file_exists( $path ) and \OC_Filesystem::filesize($path)>0){
$meta = stream_get_meta_data( $result );
// If file is encrypted, decrypt using crypto protocol
if ( Crypt::isEncryptedContent( $path ) ) {
fclose ( $result );
$result = fopen( 'crypt://'.$path, $meta['mode'] );
} elseif (
self::shouldEncrypt( $path )
and $meta ['mode'] != 'r'
and $meta['mode'] != 'rb'
) {
# TODO: figure out what this does
if (
\OC_Filesystem::file_exists( $path )
and \OC_Filesystem::filesize( $path ) > 0
) {
//first encrypt the target file so we don't end up with a half encrypted file
\OCP\Util::writeLog('files_encryption','Decrypting '.$path.' before writing', \OCP\Util::DEBUG);
$tmp=fopen('php://temp');
\OCP\Files::streamCopy($result,$tmp);
fclose($result);
\OC_Filesystem::file_put_contents($path,$tmp);
fclose($tmp);
\OCP\Util::writeLog( 'files_encryption', 'Decrypting '.$path.' before writing', \OCP\Util::DEBUG );
$tmp = fopen( 'php://temp' );
\OCP\Files::streamCopy( $result, $tmp );
// Close the original stream, we'll return another one
fclose( $result );
\OC_Filesystem::file_put_contents( $path, $tmp );
fclose( $tmp );
}
$result=fopen('crypt://'.$path,$meta['mode']);
$result = fopen( 'crypt://'.$path, $meta['mode'] );
}
return $result;
}
public function postGetMimeType($path,$mime){

View File

@ -44,18 +44,19 @@ class Util {
# DONE: add method to check if file is encrypted using old system
# DONE: add method to fetch legacy key
# DONE: add method to decrypt legacy encrypted data
# DONE: fix / test the crypt stream proxy class
# TODO: replace cryptstream wrapper with stream_socket_enable_crypto, or fix it to use new crypt class methods
# TODO: add support for optional recovery user in case of lost passphrase / keys
# TODO: add admin optional required long passphrase for users
# TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
# TODO: add UI buttons for encrypt / decrypt everything?
# TODO: add method to encrypt all user files using new system
# TODO: add method to decrypt all user files using new system
# TODO: add method to encrypt all user files using old system
# TODO: add method to decrypt all user files using old system
# TODO: fix / test the crypt stream proxy class
# TODO: add support for optional recovery user in case of lost passphrase / keys
# TODO: add admin optional required long passphrase for users
# TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
# TODO: add UI buttons for encrypt / decrypt everything?
# TODO: test new encryption with webdav
# TODO: test new encryption with versioning
# TODO: test new encryption with sharing
@ -154,6 +155,89 @@ class Util {
}
public function findFiles( $directory, $type = 'plain' ) {
# TODO: test finding non plain content
if ( $handle = $this->view->opendir( $directory ) ) {
while ( false !== ( $file = readdir( $handle ) ) ) {
if (
$file != "."
&& $file != ".."
) {
$filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file );
var_dump($filePath);
if ( $this->view->is_dir( $filePath ) ) {
$this->findFiles( $filePath );
} elseif ( $this->view->is_file( $filePath ) ) {
if ( $type == 'plain' ) {
$this->files[] = array( 'name' => $file, 'path' => $filePath );
} elseif ( $type == 'encrypted' ) {
if ( Crypt::isEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
$this->files[] = array( 'name' => $file, 'path' => $filePath );
}
} elseif ( $type == 'legacy' ) {
if ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
$this->files[] = array( 'name' => $file, 'path' => $filePath );
}
}
}
}
}
if ( !empty( $this->files ) ) {
return $this->files;
} else {
return false;
}
}
return false;
}
public function encryptAll( OC_FilesystemView $view ) {
$plainFiles = $this->findPlainFiles( $view );
if ( $this->encryptFiles( $plainFiles ) ) {
return true;
} else {
return false;
}
}
/**
* @brief Get the blowfish encryption handeler for a key
* @param $key string (optional)