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:
parent
f6863f9e51
commit
84fd62b130
|
@ -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( '/' );
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue