]> source.dussan.org Git - nextcloud-server.git/commitdiff
initial integration of encryption
authorRobin Appelman <icewind1991@gmail.com>
Thu, 24 Nov 2011 00:44:54 +0000 (01:44 +0100)
committerRobin Appelman <icewind@owncloud.com>
Tue, 21 Feb 2012 19:48:48 +0000 (20:48 +0100)
apps/files_encryption/appinfo/info.xml [new file with mode: 0644]
apps/files_encryption/lib/crypt.php [new file with mode: 0644]
apps/files_encryption/lib/cryptstream.php
apps/files_encryption/lib/proxy.php
lib/crypt.php [deleted file]
lib/user.php

diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
new file mode 100644 (file)
index 0000000..a8db06a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<info>
+       <id>files_encryption</id>
+       <name>Encryption</name>
+       <description>Server side encryption of files</description>
+       <version>0.1</version>
+       <licence>AGPL</licence>
+       <author>Robin Appelman</author>
+       <require>3</require>
+       <default_enable/>
+</info>
diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php
new file mode 100644 (file)
index 0000000..4e7739b
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @copyright 2010 Frank Karlitschek karlitschek@kde.org
+ *
+ * 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/>.
+ *
+ */
+
+
+
+// Todo:
+//  - Crypt/decrypt button in the userinterface
+//  - Setting if crypto should be on by default
+//  - Add a setting "Don´t encrypt files larger than xx because of performance reasons"
+//  - Transparent decrypt/encrpt in filesystem.php. Autodetect if a file is encrypted (.encrypted extensio)
+//  - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster
+//  - IMPORTANT! Check if the block lenght of the encrypted data stays the same
+
+
+require_once('Crypt_Blowfish/Blowfish.php');
+
+/**
+ * This class is for crypting and decrypting
+ */
+class OC_Crypt {
+       static private $bf = null;
+
+       public static function loginListener($params){
+               self::init($params['uid'],$params['password']);
+       }
+
+       public static function init($login,$password) {
+               if(OC_User::isLoggedIn()){
+                       $view=new OC_FilesystemView('/'.$login);
+                       if(!$view->file_exists('/encryption.key')){// does key exist?
+                               OC_Crypt::createkey($password);
+                       }
+                       $key=$view->file_get_contents('/encryption.key');
+                       $_SESSION['enckey']=OC_Crypt::decrypt($key, $password);
+               }
+       }
+
+       /**
+        * get the blowfish encryption handeler for a key
+        * @param string $key (optional)
+        *
+        * if the key is left out, the default handeler will be used
+        */
+       public static function getBlowfish($key=''){
+               if($key){
+                       return new Crypt_Blowfish($key);
+               }else{
+                       if(!isset($_SESSION['enckey'])){
+                               return false;
+                       }
+                       if(!self::$bf){
+                               self::$bf=new Crypt_Blowfish($_SESSION['enckey']);
+                       }
+                       return self::$bf;
+               }
+       }
+
+       public static function createkey($passcode) {
+               if(OC_User::isLoggedIn()){
+                       // generate a random key
+                       $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999);
+
+                       // encrypt the key with the passcode of the user
+                       $enckey=OC_Crypt::encrypt($key,$passcode);
+
+                       // Write the file
+                       $username=OC_USER::getUser();
+                       OC_FileProxy::$enabled=false;
+                       $view=new OC_FilesystemView('/'.$username);
+                       $view->file_put_contents('/encryption.key',$enckey);
+                       OC_FileProxy::$enabled=true;
+               }
+       }
+
+       public static function changekeypasscode($oldPassword, $newPassword) {
+               if(OC_User::isLoggedIn()){
+                       $username=OC_USER::getUser();
+                       $view=new OC_FilesystemView('/'.$username);
+
+                       // read old key
+                       $key=$view->file_get_contents('/encryption.key');
+
+                       // decrypt key with old passcode
+                       $key=OC_Crypt::decrypt($key, $oldPassword);
+
+                       // encrypt again with new passcode
+                       $key=OC_Crypt::encrypt($key, $newPassword);
+
+                       // store the new key
+                       $view->file_put_contents('/encryption.key', $key );
+               }
+       }
+
+       /**
+        * @brief encrypts an content
+        * @param $content the cleartext message you want to encrypt
+        * @param $key the encryption key (optional)
+        * @returns encrypted content
+        *
+        * This function encrypts an content
+        */
+       public static function encrypt( $content, $key='') {
+               $bf = self::getBlowfish($key);
+               return($bf->encrypt($content));
+       }
+
+       /**
+       * @brief decryption of an content
+       * @param $content the cleartext message you want to decrypt
+       * @param $key the encryption key (optional)
+       * @returns cleartext content
+       *
+       * This function decrypts an content
+       */
+       public static function decrypt( $content, $key='') {
+               $bf = self::getBlowfish($key);
+               return($bf->encrypt($contents));
+       }
+
+       /**
+       * @brief encryption of a file
+       * @param $filename
+       * @param $key the encryption key
+       *
+       * This function encrypts a file
+       */
+       public static function encryptfile( $filename, $key) {
+               $handleread  = fopen($filename, "rb");
+               if($handleread<>FALSE) {
+                       $handlewrite = fopen($filename.OC_Crypt::$encription_extension, "wb");
+                       while (!feof($handleread)) {
+                               $content = fread($handleread, 8192);
+                               $enccontent=OC_CRYPT::encrypt( $content, $key);
+                               fwrite($handlewrite, $enccontent);
+                       }
+                       fclose($handlewrite);
+                       unlink($filename);
+               }
+               fclose($handleread);
+       }
+
+
+        /**
+         * @brief decryption of a file
+         * @param $filename
+         * @param $key the decryption key
+         *
+         * This function decrypts a file
+         */
+       public static function decryptfile( $filename, $key) {
+               $handleread  = fopen($filename.OC_Crypt::$encription_extension, "rb");
+               if($handleread<>FALSE) {
+                       $handlewrite = fopen($filename, "wb");
+                       while (!feof($handleread)) {
+                               $content = fread($handleread, 8192);
+                               $enccontent=OC_CRYPT::decrypt( $content, $key);
+                               fwrite($handlewrite, $enccontent);
+                       }
+                       fclose($handlewrite);
+                       unlink($filename.OC_Crypt::$encription_extension);
+               }
+               fclose($handleread);
+       }
+       
+       /**
+        * encrypt data in 8192b sized blocks
+        */
+       public static function blockEncrypt($data){
+               $result='';
+               while(strlen($data)){
+                       $result=self::encrypt(substr($data,0,8192));
+                       $data=substr($data,8192);
+               }
+               return $result;
+       }
+       
+       /**
+        * decrypt data in 8192b sized blocks
+        */
+       public static function blockDecrypt($data){
+               $result='';
+               while(strlen($data)){
+                       $result=self::decrypt(substr($data,0,8192));
+                       $data=substr($data,8192);
+               }
+               return $result;
+       }
+}
index 7fbfeaa7a86a48cfc2d19853f34a9e8755c5f8d2..00dda7352b3f8c540d27b99b05e5db8c39dc1f30 100644 (file)
 
 /**
  * transparently encrypted filestream
+ *
+ * you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream)
+ *   and then fopen('crypt://streams/foo');
  */
 
 class OC_CryptStream{
+       public static $sourceStreams=array();
        private $source;
+       private $path;
+       private $readBuffer;//for streams that dont support seeking
+       private $meta=array();//header/meta for source stream
 
        public function stream_open($path, $mode, $options, &$opened_path){
                $path=str_replace('crypt://','',$path);
-               OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG);
-               OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
-               $this->source=OC_FileSystem::fopen($path,$mode);
-               OC_FileProxy::$enabled=true;
-               if(!is_resource($this->source)){
-                       OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR);
+               if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){
+                       $this->source=self::$sourceStreams[basename($path)]['stream'];
+                       $this->path=self::$sourceStreams[basename($path)]['path'];
+               }else{
+                       $this->path=$path;
+                       OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG);
+                       OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
+                       $this->source=OC_FileSystem::fopen($path,$mode);
+                       OC_FileProxy::$enabled=true;
+                       if(!is_resource($this->source)){
+                               OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR);
+                       }
+               }
+               if(is_resource($this->source)){
+                       $this->meta=stream_get_meta_data($this->source);
                }
                return is_resource($this->source);
        }
@@ -51,14 +67,26 @@ class OC_CryptStream{
                $pos=0;
                $currentPos=ftell($this->source);
                $offset=$currentPos%8192;
-               fseek($this->source,-$offset,SEEK_CUR);
                $result='';
+               if($offset>0){
+                       if($this->meta['seekable']){
+                               fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used
+                       }else{
+                               $pos=strlen($this->readBuffer);
+                               $result=$this->readBuffer;
+                       }
+               }
                while($count>$pos){
                        $data=fread($this->source,8192);
                        $pos+=8192;
-                       $result.=OC_Crypt::decrypt($data);
+                       if(strlen($data)){
+                               $result.=OC_Crypt::decrypt($data);
+                       }
                }
-               return substr($result,$offset,$count);
+               if(!$this->meta['seekable']){
+                       $this->readBuffer=substr($result,$count);
+               }
+               return substr($result,0,$count);
        }
        
        public function stream_write($data){
@@ -119,6 +147,9 @@ class OC_CryptStream{
        }
 
        public function stream_close(){
+               if(OC_FileCache::inCache($this->path)){
+                       OC_FileCache::put($this->path,array('encrypted'=>true));
+               }
                return fclose($this->source);
        }
 }
\ No newline at end of file
index 053ac786c3392f484209bd1900825e7c30af6d23..173aea785ecc7702cd7ba5c3a9701195833a79f9 100644 (file)
  */
 
 class OC_FileProxy_Encryption extends OC_FileProxy{
+       private static $blackList=null; //mimetypes blacklisted from encryption
+       private static $metaData=array(); //metadata cache
+       
+       /**
+        * check if a file should be encrypted during write
+        * @param string $path
+        * @return bool
+        */
+       private static function shouldEncrypt($path){
+               if(is_null(self::$blackList)){
+                       self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
+               }
+               if(isset(self::$metaData[$path])){
+                       $metadata=self::$metaData[$path];
+               }else{
+                       $metadata=OC_FileCache::get($path);
+                       self::$metaData[$path]=$metadata;
+               }
+               if($metadata['encrypted']){
+                       return true;
+               }
+               $extention=substr($path,strrpos($path,'.')+1);
+               if(array_search($extention,self::$blackList)===false){
+                       return true;
+               }
+       }
+
+       /**
+        * check if a file is encrypted
+        * @param string $path
+        * @return bool
+        */
+       private static function isEncrypted($path){
+               if(isset(self::$metaData[$path])){
+                       $metadata=self::$metaData[$path];
+               }else{
+                       $metadata=OC_FileCache::get($path);
+                       self::$metaData[$path]=$metadata;
+               }
+               return (bool)$metadata['encrypted'];
+       }
+       
        public function preFile_put_contents($path,&$data){
-               if(substr($path,-4)=='.enc'){
+               if(self::shouldEncrypt($path)){
+                       $exists=OC_Filesystem::file_exists($path);
+                       $target=fopen('crypt://'.$path,'w');
                        if (is_resource($data)) {
-                               $newData='';
                                while(!feof($data)){
-                                       $block=fread($data,8192);
-                                       $newData.=OC_Crypt::encrypt($block);
+                                       fwrite($target,fread($data,8192));
                                }
-                               $data=$newData;
                        }else{
-                               $data=OC_Crypt::blockEncrypt($data);
+                               fwrite($target,$data);
                        }
+                       //fake the normal hooks
+                       OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path));
+                       OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path));
+                       return false;
                }
        }
        
        public function postFile_get_contents($path,$data){
-               if(substr($path,-4)=='.enc'){
-                       return OC_Crypt::blockDecrypt($data);
+               if(self::isEncrypted($path)){
+                       $data=OC_Crypt::blockDecrypt($data);
                }
+               return $data;
        }
        
        public function postFopen($path,&$result){
-               if(substr($path,-4)=='.enc'){
-                       $meta=stream_get_meta_data($result);
+               if(!$result){
+                       return $result;
+               }
+               $meta=stream_get_meta_data($result);
+               if(self::isEncrypted($path)){
                        fclose($result);
-                       OC_log::write('file_encryption','mode: '.$meta['mode']);
+                       $result=fopen('crypt://'.$path,$meta['mode']);
+               }elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){
+                       if(OC_Filesystem::file_exists($path)){
+                               //first encrypt the target file so we don't end up with a half encrypted file
+                               OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG);
+                               if($result){
+                                       fclose($result);
+                               }
+                               $tmpFile=OC_Filesystem::toTmpFile($path);
+                               OC_Filesystem::fromTmpFile($tmpFile,$path);
+                       }
                        $result=fopen('crypt://'.$path,$meta['mode']);
                }
+               return $result;
        }
        
        public function preReadFile($path){
-               if(substr($path,-4)=='.enc'){
+               if(self::isEncrypted($path)){
                        $stream=fopen('crypt://'.$path,'r');
                        while(!feof($stream)){
                                print(fread($stream,8192));
@@ -65,11 +125,4 @@ class OC_FileProxy_Encryption extends OC_FileProxy{
                        return false;//cancel the original request
                }
        }
-       
-       public function postGetMimeType($path,$result){
-               if(substr($path,-4)=='.enc'){
-                       return 'text/plain';
-               }
-               return $result;
-       }
 }
diff --git a/lib/crypt.php b/lib/crypt.php
deleted file mode 100644 (file)
index 3e6fa05..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-/**
- * ownCloud
- *
- * @author Frank Karlitschek
- * @copyright 2010 Frank Karlitschek karlitschek@kde.org
- *
- * 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/>.
- *
- */
-
-
-
-// Todo:
-//  - Crypt/decrypt button in the userinterface
-//  - Setting if crypto should be on by default
-//  - Add a setting "Don´t encrypt files larger than xx because of performance reasons"
-//  - Transparent decrypt/encrpt in filesystem.php. Autodetect if a file is encrypted (.encrypted extensio)
-//  - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster
-//  - IMPORTANT! Check if the block lenght of the encrypted data stays the same
-
-
-require_once('Crypt_Blowfish/Blowfish.php');
-
-/**
- * This class is for crypting and decrypting
- */
-class OC_Crypt {
-
-        static $encription_extension='.encrypted';
-
-       public static function init($login,$password) {
-               $_SESSION['user_password'] = $password;  // save the password as passcode for the encryption
-               if(OC_User::isLoggedIn()){
-                       // does key exist?
-                       if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){
-                               OC_Crypt::createkey($_SESSION['user_password']);
-                       }
-               }
-       }
-
-
-
-       public static function createkey($passcode) {
-               if(OC_User::isLoggedIn()){
-                       // generate a random key
-                       $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999);
-
-                       // encrypt the key with the passcode of the user
-                       $enckey=OC_Crypt::encrypt($key,$passcode);
-
-                       // Write the file
-                       $username=OC_USER::getUser();
-                       @file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey );
-               }
-       }
-
-       public static function changekeypasscode( $newpasscode) {
-               if(OC_User::isLoggedIn()){
-                       $username=OC_USER::getUser();
-
-                       // read old key
-                       $key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key');
-
-                       // decrypt key with old passcode
-                       $key=OC_Crypt::decrypt($key, $_SESSION['user_password']);
-
-                       // encrypt again with new passcode
-                       $key=OC_Crypt::encrypt($key,$newpassword);
-
-                       // store the new key
-                       file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key );
-
-                        $_SESSION['user_password']=$newpasscode;
-               }
-       }
-
-       /**
-        * @brief encrypts an content
-        * @param $content the cleartext message you want to encrypt
-        * @param $key the encryption key
-        * @returns encrypted content
-        *
-        * This function encrypts an content
-        */
-       public static function encrypt( $content, $key) {
-               $bf = new Crypt_Blowfish($key);
-               return($bf->encrypt($content));
-       }
-
-
-        /**
-         * @brief decryption of an content
-         * @param $content the cleartext message you want to decrypt
-         * @param $key the encryption key
-         * @returns cleartext content
-         *
-         * This function decrypts an content
-         */
-        public static function decrypt( $content, $key) {
-               $bf = new Crypt_Blowfish($key);
-               return($bf->encrypt($contents));
-        }       
-
-       /**
-       * @brief encryption of a file
-       * @param $filename
-       * @param $key the encryption key
-       *
-       * This function encrypts a file
-       */
-       public static function encryptfile( $filename, $key) {
-               $handleread  = fopen($filename, "rb");
-               if($handleread<>FALSE) {
-                       $handlewrite = fopen($filename.OC_Crypt::$encription_extension, "wb");
-                       while (!feof($handleread)) {
-                               $content = fread($handleread, 8192);
-                               $enccontent=OC_CRYPT::encrypt( $content, $key);
-                               fwrite($handlewrite, $enccontent);
-                       }
-                       fclose($handlewrite);
-                       unlink($filename);
-               }
-               fclose($handleread);
-       }
-
-
-        /**
-         * @brief decryption of a file
-         * @param $filename
-         * @param $key the decryption key
-         *
-         * This function decrypts a file
-         */
-       public static function decryptfile( $filename, $key) {
-               $handleread  = fopen($filename.OC_Crypt::$encription_extension, "rb");
-               if($handleread<>FALSE) {
-                       $handlewrite = fopen($filename, "wb");
-                       while (!feof($handleread)) {
-                               $content = fread($handleread, 8192);
-                               $enccontent=OC_CRYPT::decrypt( $content, $key);
-                               fwrite($handlewrite, $enccontent);
-                       }
-                       fclose($handlewrite);
-                       unlink($filename.OC_Crypt::$encription_extension);
-               }
-               fclose($handleread);
-       }
-       
-       /**
-        * encrypt data in 8192b sized blocks
-        */
-       public static function blockEncrypt($data){
-               $result='';
-               while(strlen($data)){
-                       $result=self::encrypt(substr($data,0,8192));
-                       $data=substr($data,8192);
-               }
-               return $result;
-       }
-       
-       /**
-        * decrypt data in 8192b sized blocks
-        */
-       public static function blockDecrypt($data){
-               $result='';
-               while(strlen($data)){
-                       $result=self::decrypt(substr($data,0,8192));
-                       $data=substr($data,8192);
-               }
-               return $result;
-       }
-
-
-
-
-}
index 34f44f572e0b1b533ff125dd4830eaec531542e4..aa828de52f5a84bdd401ef85808b8c42d1bc022f 100644 (file)
@@ -195,8 +195,8 @@ class OC_User {
                if( $run ){
                        $uid=self::checkPassword( $uid, $password );
                        if($uid){
-                               OC_Crypt::init($uid,$password);
                                return self::setUserId($uid);
+                               OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password ));
                        }
                }
                return false;
@@ -209,7 +209,6 @@ class OC_User {
         */
        public static function setUserId($uid) {
                $_SESSION['user_id'] = $uid;
-               OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid ));
                return true;
        }