summaryrefslogtreecommitdiffstats
path: root/apps/files_encryption
diff options
context:
space:
mode:
authorRobin Appelman <icewind1991@gmail.com>2011-11-24 01:44:54 +0100
committerRobin Appelman <icewind@owncloud.com>2012-02-21 20:48:48 +0100
commitf1cbb9effc7e0672dd9dd6fa810aba36c5749898 (patch)
treed4c0625ffc75ab2c25ed9b7e03502110af05667a /apps/files_encryption
parente53e7990c404e3ff2a1b7abad1e4c8ad4f89ee2a (diff)
downloadnextcloud-server-f1cbb9effc7e0672dd9dd6fa810aba36c5749898.tar.gz
nextcloud-server-f1cbb9effc7e0672dd9dd6fa810aba36c5749898.zip
initial integration of encryption
Diffstat (limited to 'apps/files_encryption')
-rw-r--r--apps/files_encryption/appinfo/info.xml11
-rw-r--r--apps/files_encryption/lib/crypt.php207
-rw-r--r--apps/files_encryption/lib/cryptstream.php49
-rw-r--r--apps/files_encryption/lib/proxy.php91
4 files changed, 330 insertions, 28 deletions
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
new file mode 100644
index 00000000000..a8db06aa3db
--- /dev/null
+++ b/apps/files_encryption/appinfo/info.xml
@@ -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
index 00000000000..4e7739b1d06
--- /dev/null
+++ b/apps/files_encryption/lib/crypt.php
@@ -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;
+ }
+}
diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php
index 7fbfeaa7a86..00dda7352b3 100644
--- a/apps/files_encryption/lib/cryptstream.php
+++ b/apps/files_encryption/lib/cryptstream.php
@@ -22,19 +22,35 @@
/**
* 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
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
index 053ac786c33..173aea785ec 100644
--- a/apps/files_encryption/lib/proxy.php
+++ b/apps/files_encryption/lib/proxy.php
@@ -26,38 +26,98 @@
*/
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;
- }
}