Merge branch 'master' into fix_for_2377

This commit is contained in:
Florin Peter 2013-05-24 20:54:13 +02:00
commit 946e9ccc0a
733 changed files with 12482 additions and 7077 deletions

2
.gitignore vendored
View File

@ -76,4 +76,4 @@ nbproject
data-autotest
/tests/coverage*
/tests/autoconfig*
/tests/autotest*
/tests/autotest*

View File

@ -46,6 +46,7 @@
"{count} folders" => "{count} φάκελοι",
"1 file" => "1 αρχείο",
"{count} files" => "{count} αρχεία",
"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Μη έγκυρο όνομα φακέλου. Η χρήση του 'Κοινόχρηστος' χρησιμοποιείται από το ownCloud",
"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου",
"Upload" => "Μεταφόρτωση",
"File handling" => "Διαχείριση αρχείων",

View File

@ -1,3 +1,16 @@
<?php $TRANSLATIONS = array(
"Error" => "Ошибка"
"No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка",
"There is no error, the file uploaded with success" => "Ошибки нет, файл успешно загружен",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Размер загружаемого файла превысил максимально допустимый в директиве MAX_FILE_SIZE, специфицированной в HTML-форме",
"The uploaded file was only partially uploaded" => "Загружаемый файл был загружен лишь частично",
"No file was uploaded" => "Файл не был загружен",
"Missing a temporary folder" => "Отсутствие временной папки",
"Failed to write to disk" => "Не удалось записать на диск",
"Not enough storage available" => "Недостаточно места в хранилище",
"Share" => "Сделать общим",
"Delete" => "Удалить",
"Error" => "Ошибка",
"Name" => "Имя",
"Save" => "Сохранить",
"Download" => "Загрузка"
);

View File

@ -46,6 +46,7 @@
"{count} folders" => "{count} priečinkov",
"1 file" => "1 súbor",
"{count} files" => "{count} súborov",
"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Neplatný názov priečinka. Názov \"Shared\" je rezervovaný pre ownCloud",
"Unable to rename file" => "Nemožno premenovať súbor",
"Upload" => "Odoslať",
"File handling" => "Nastavenie správania sa k súborom",

View File

@ -46,6 +46,7 @@
"{count} folders" => "{count} dizin",
"1 file" => "1 dosya",
"{count} files" => "{count} dosya",
"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Geçersiz dizin adı. 'Shared' dizin ismi kullanımı ownCloud tarafından rezerve edilmiştir.",
"Unable to rename file" => "Dosya adı değiştirilemedi",
"Upload" => "Yükle",
"File handling" => "Dosya taşıma",

View File

@ -1,5 +1,5 @@
<?php if(count($_["breadcrumb"])):?>
<div class="crumb">
<div class="crumb" data-dir=''>
<a href="<?php print_unescaped($_['baseURL']); ?>">
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
</a>

View File

@ -0,0 +1,317 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Crypt_Blowfish allows for encryption and decryption on the fly using
* the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
* PHP extension, it uses only PHP.
* Crypt_Blowfish support encryption/decryption with or without a secret key.
*
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Blowfish.php,v 1.81 2005/05/30 18:40:36 mfonda Exp $
* @link http://pear.php.net/package/Crypt_Blowfish
*/
require_once 'PEAR.php';
/**
*
* Example usage:
* $bf = new Crypt_Blowfish('some secret key!');
* $encrypted = $bf->encrypt('this is some example plain text');
* $plaintext = $bf->decrypt($encrypted);
* echo "plain text: $plaintext";
*
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/Crypt_Blowfish
* @version @package_version@
* @access public
*/
class Crypt_Blowfish
{
/**
* P-Array contains 18 32-bit subkeys
*
* @var array
* @access private
*/
var $_P = array();
/**
* Array of four S-Blocks each containing 256 32-bit entries
*
* @var array
* @access private
*/
var $_S = array();
/**
* Mcrypt td resource
*
* @var resource
* @access private
*/
var $_td = null;
/**
* Initialization vector
*
* @var string
* @access private
*/
var $_iv = null;
/**
* Crypt_Blowfish Constructor
* Initializes the Crypt_Blowfish object, and gives a sets
* the secret key
*
* @param string $key
* @access public
*/
function Crypt_Blowfish($key)
{
if (extension_loaded('mcrypt')) {
$this->_td = mcrypt_module_open(MCRYPT_BLOWFISH, '', 'ecb', '');
$this->_iv = mcrypt_create_iv(8, MCRYPT_RAND);
}
$this->setKey($key);
}
/**
* Deprecated isReady method
*
* @return bool
* @access public
* @deprecated
*/
function isReady()
{
return true;
}
/**
* Deprecated init method - init is now a private
* method and has been replaced with _init
*
* @return bool
* @access public
* @deprecated
* @see Crypt_Blowfish::_init()
*/
function init()
{
$this->_init();
}
/**
* Initializes the Crypt_Blowfish object
*
* @access private
*/
function _init()
{
$defaults = new Crypt_Blowfish_DefaultKey();
$this->_P = $defaults->P;
$this->_S = $defaults->S;
}
/**
* Enciphers a single 64 bit block
*
* @param int &$Xl
* @param int &$Xr
* @access private
*/
function _encipher(&$Xl, &$Xr)
{
for ($i = 0; $i < 16; $i++) {
$temp = $Xl ^ $this->_P[$i];
$Xl = ((($this->_S[0][($temp>>24) & 255] +
$this->_S[1][($temp>>16) & 255]) ^
$this->_S[2][($temp>>8) & 255]) +
$this->_S[3][$temp & 255]) ^ $Xr;
$Xr = $temp;
}
$Xr = $Xl ^ $this->_P[16];
$Xl = $temp ^ $this->_P[17];
}
/**
* Deciphers a single 64 bit block
*
* @param int &$Xl
* @param int &$Xr
* @access private
*/
function _decipher(&$Xl, &$Xr)
{
for ($i = 17; $i > 1; $i--) {
$temp = $Xl ^ $this->_P[$i];
$Xl = ((($this->_S[0][($temp>>24) & 255] +
$this->_S[1][($temp>>16) & 255]) ^
$this->_S[2][($temp>>8) & 255]) +
$this->_S[3][$temp & 255]) ^ $Xr;
$Xr = $temp;
}
$Xr = $Xl ^ $this->_P[1];
$Xl = $temp ^ $this->_P[0];
}
/**
* Encrypts a string
*
* @param string $plainText
* @return string Returns cipher text on success, PEAR_Error on failure
* @access public
*/
function encrypt($plainText)
{
if (!is_string($plainText)) {
PEAR::raiseError('Plain text must be a string', 0, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
return mcrypt_generic($this->_td, $plainText);
}
$cipherText = '';
$len = strlen($plainText);
$plainText .= str_repeat(chr(0),(8 - ($len%8))%8);
for ($i = 0; $i < $len; $i += 8) {
list(,$Xl,$Xr) = unpack("N2",substr($plainText,$i,8));
$this->_encipher($Xl, $Xr);
$cipherText .= pack("N2", $Xl, $Xr);
}
return $cipherText;
}
/**
* Decrypts an encrypted string
*
* @param string $cipherText
* @return string Returns plain text on success, PEAR_Error on failure
* @access public
*/
function decrypt($cipherText)
{
if (!is_string($cipherText)) {
PEAR::raiseError('Cipher text must be a string', 1, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
return mdecrypt_generic($this->_td, $cipherText);
}
$plainText = '';
$len = strlen($cipherText);
$cipherText .= str_repeat(chr(0),(8 - ($len%8))%8);
for ($i = 0; $i < $len; $i += 8) {
list(,$Xl,$Xr) = unpack("N2",substr($cipherText,$i,8));
$this->_decipher($Xl, $Xr);
$plainText .= pack("N2", $Xl, $Xr);
}
return $plainText;
}
/**
* Sets the secret key
* The key must be non-zero, and less than or equal to
* 56 characters in length.
*
* @param string $key
* @return bool Returns true on success, PEAR_Error on failure
* @access public
*/
function setKey($key)
{
if (!is_string($key)) {
PEAR::raiseError('Key must be a string', 2, PEAR_ERROR_DIE);
}
$len = strlen($key);
if ($len > 56 || $len == 0) {
PEAR::raiseError('Key must be less than 56 characters and non-zero. Supplied key length: ' . $len, 3, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
mcrypt_generic_init($this->_td, $key, $this->_iv);
return true;
}
require_once 'Blowfish/DefaultKey.php';
$this->_init();
$k = 0;
$data = 0;
$datal = 0;
$datar = 0;
for ($i = 0; $i < 18; $i++) {
$data = 0;
for ($j = 4; $j > 0; $j--) {
$data = $data << 8 | ord($key{$k});
$k = ($k+1) % $len;
}
$this->_P[$i] ^= $data;
}
for ($i = 0; $i <= 16; $i += 2) {
$this->_encipher($datal, $datar);
$this->_P[$i] = $datal;
$this->_P[$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[0][$i] = $datal;
$this->_S[0][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[1][$i] = $datal;
$this->_S[1][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[2][$i] = $datal;
$this->_S[2][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[3][$i] = $datal;
$this->_S[3][$i+1] = $datar;
}
return true;
}
}
?>

View File

@ -0,0 +1,327 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Crypt_Blowfish allows for encryption and decryption on the fly using
* the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
* PHP extension, it uses only PHP.
* Crypt_Blowfish support encryption/decryption with or without a secret key.
*
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: DefaultKey.php,v 1.81 2005/05/30 18:40:37 mfonda Exp $
* @link http://pear.php.net/package/Crypt_Blowfish
*/
/**
* Class containing default key
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/Crypt_Blowfish
* @version @package_version@
* @access public
*/
class Crypt_Blowfish_DefaultKey
{
var $P = array();
var $S = array();
function Crypt_Blowfish_DefaultKey()
{
$this->P = array(
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B
);
$this->S = array(
array(
0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
),
array(
0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
),
array(
0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
),
array(
0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
)
);
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*
* @brief Script to handle admin settings for encrypted key recovery
*/
use OCA\Encryption;
\OCP\JSON::checkAdminUser();
\OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck();
$l=OC_L10N::get('files_encryption');
$return = false;
// Enable recoveryAdmin
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] == 1){
$return = \OCA\Encryption\Helper::adminEnableRecovery($recoveryKeyId, $_POST['recoveryPassword']);
$action = "enable";
// Disable recoveryAdmin
} elseif (
isset($_POST['adminEnableRecovery'])
&& 0 == $_POST['adminEnableRecovery']
) {
$return = \OCA\Encryption\Helper::adminDisableRecovery($_POST['recoveryPassword']);
$action = "disable";
}
// Return success or failure
if ($return) {
\OCP\JSON::success(array("data" => array( "message" => $l->t('Recovery key successfully ' . $action.'d'))));
} else {
\OCP\JSON::error(array("data" => array( "message" => $l->t('Could not '.$action.' recovery key. Please check your recovery key password!'))));
}

View File

@ -0,0 +1,52 @@
<?php
/**
* Copyright (c) 2013, Bjoern Schiessle <schiessle@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*
* @brief Script to change recovery key password
*
*/
use OCA\Encryption;
\OCP\JSON::checkAdminUser();
\OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck();
$l=OC_L10N::get('core');
$return = false;
$oldPassword = $_POST['oldPassword'];
$newPassword = $_POST['newPassword'];
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$result = $util->checkRecoveryPassword($oldPassword);
if ($result) {
$keyId = $util->getRecoveryKeyId();
$keyPath = '/owncloud_private_key/' . $keyId . ".private.key";
$view = new \OC\Files\View('/');
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptedRecoveryKey = $view->file_get_contents($keyPath);
$decryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricDecryptFileContent($encryptedRecoveryKey, $oldPassword);
$encryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword);
$view->file_put_contents($keyPath, $encryptedRecoveryKey);
\OC_FileProxy::$enabled = $proxyStatus;
$return = true;
}
// success or failure
if ($return) {
\OCP\JSON::success(array("data" => array( "message" => $l->t('Password successfully changed.'))));
} else {
\OCP\JSON::error(array("data" => array( "message" => $l->t('Could not change the password. Maybe the old password was not correct.'))));
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*
* @brief Script to handle admin settings for encrypted key recovery
*/
use OCA\Encryption;
\OCP\JSON::checkLoggedIn();
\OCP\JSON::checkAppEnabled( 'files_encryption' );
\OCP\JSON::callCheck();
if (
isset( $_POST['userEnableRecovery'] )
&& ( 0 == $_POST['userEnableRecovery'] || 1 == $_POST['userEnableRecovery'] )
) {
$userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new \OCA\Encryption\Util( $view, $userId );
// Save recovery preference to DB
$return = $util->setRecoveryForUser( $_POST['userEnableRecovery'] );
if ($_POST['userEnableRecovery'] == "1") {
$util->addRecoveryKeys();
} else {
$util->removeRecoveryKeys();
}
} else {
$return = false;
}
// Return success or failure
( $return ) ? \OCP\JSON::success() : \OCP\JSON::error();

View File

@ -8,42 +8,44 @@ OC::$CLASSPATH['OCA\Encryption\Stream'] = 'files_encryption/lib/stream.php';
OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php';
OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
OC_FileProxy::register( new OCA\Encryption\Proxy() );
// User-related hooks
OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
// User related hooks
OCA\Encryption\Helper::registerUserHooks();
// Sharing-related hooks
OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' );
OCP\Util::connectHook( 'OCP\Share', 'pre_unshare', 'OCA\Encryption\Hooks', 'preUnshare' );
OCP\Util::connectHook( 'OCP\Share', 'pre_unshareAll', 'OCA\Encryption\Hooks', 'preUnshareAll' );
// Sharing related hooks
OCA\Encryption\Helper::registerShareHooks();
// Webdav-related hooks
OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfile' );
// Filesystem related hooks
OCA\Encryption\Helper::registerFilesystemHooks();
stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' );
$session = new OCA\Encryption\Session();
// check if we are logged in
if (OCP\User::isLoggedIn()) {
$view = new OC_FilesystemView('/');
$session = new \OCA\Encryption\Session($view);
if (
! $session->getPrivateKey( \OCP\USER::getUser() )
&& OCP\User::isLoggedIn()
&& OCA\Encryption\Crypt::mode() == 'server'
) {
// check if user has a private key
if (
!$session->getPrivateKey(\OCP\USER::getUser())
&& OCA\Encryption\Crypt::mode() === 'server'
) {
// Force the user to log-in again if the encryption key isn't unlocked
// (happens when a user is logged in before the encryption app is
// enabled)
OCP\User::logout();
header( "Location: " . OC::$WEBROOT.'/' );
exit();
// Force the user to log-in again if the encryption key isn't unlocked
// (happens when a user is logged in before the encryption app is
// enabled)
OCP\User::logout();
header("Location: " . OC::$WEBROOT . '/');
exit();
}
}
// Register settings scripts
OCP\App::registerAdmin( 'files_encryption', 'settings' );
OCP\App::registerAdmin( 'files_encryption', 'settings-admin' );
OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );

View File

@ -18,6 +18,21 @@
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<comments>What client-side / server-side configuration is used</comments>
</field>
<field>
<name>recovery_enabled</name>
<type>integer</type>
<notnull>true</notnull>
<default>0</default>
<comments>Whether encryption key recovery is enabled</comments>
</field>
<field>
<name>migration_status</name>
<type>integer</type>
<notnull>true</notnull>
<default>0</default>
<comments>Whether encryption migration has been performed</comments>
</field>
</declaration>
</table>

View File

@ -4,7 +4,7 @@
<name>Encryption</name>
<description>Server side encryption of files. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP.</description>
<licence>AGPL</licence>
<author>Sam Tuke</author>
<author>Sam Tuke, Bjoern Schiessle, Florin Peter</author>
<require>4</require>
<shipped>true</shipped>
<types>

View File

@ -9,6 +9,57 @@ Encrypted files
[encrypted data string][delimiter][IV][padding]
[anhAAjAmcGXqj1X9g==][00iv00][MSHU5N5gECP7aAg7][xx] (square braces added)
- Directory structure:
- Encrypted user data (catfiles) are stored in the usual /data/user/files dir
- Keyfiles are stored in /data/user/files_encryption/keyfiles
- Sharekey are stored in /data/user/files_encryption/share-files
- File extensions:
- Catfiles have to keep the file extension of the original file, pre-encryption
- Keyfiles use .keyfile
- Sharekeys have .shareKey
Shared files
------------
Shared files have a centrally stored catfile and keyfile, and one sharekey for
each user that shares it.
When sharing is used, a different encryption method is used to encrypt the
keyfile (openssl_seal). Although shared files have a keyfile, its contents
use a different format therefore.
Each time a shared file is edited or deleted, all sharekeys for users sharing
that file must have their sharekeys changed also. The keyfile and catfile
however need only changing in the owners files, as there is only one copy of
these.
Publicly shared files (public links)
------------------------------------
Files shared via public links use a separate system user account called 'ownCloud'. All public files are shared to that user's public key, and the private key is used to access the files when the public link is used in browser.
This means that files shared via public links are accessible only to users who know the shared URL, or to admins who know the 'ownCloud' user password.
Lost password recovery
----------------------
In order to enable users to read their encrypted files in the event of a password loss/reset scenario, administrators can choose to enable a 'recoveryAdmin' account. This is a user that all user files will automatically be shared to of the option is enabled. This allows the recoveryAdmin user to generate new keyfiles for the user. By default the UID of the recoveryAdmin is 'recoveryAdmin'.
OC_FilesystemView
-----------------
files_encryption deals extensively with paths and the filesystem. In order to minimise bugs, it makes calls to filesystem methods in a consistent way: OC_FilesystemView{} objects always use '/' as their root, and specify paths each time particular methods are called. e.g. do this:
$view->file_exists( 'path/to/file' );
Not:
$view->chroot( 'path/to' );
$view->file_exists( 'file' );
Using this convention means that $view objects are more predictable and less likely to break. Problems with paths are the #1 cause of bugs in this app, and consistent $view handling is an important way to prevent them.
Notes
-----
@ -16,4 +67,11 @@ Notes
- The user passphrase is required in order to set up or upgrade the app. New
keypair generation, and the re-encryption of legacy encrypted files requires
it. Therefore an appinfo/update.php script cannot be used, and upgrade logic
is handled in the login hook listener.
is handled in the login hook listener. Therefore each time the user logs in
their files are scanned to detect unencrypted and legacy encrypted files, and
they are (re)encrypted as necessary. This may present a performance issue; we
need to monitor this.
- When files are saved to ownCloud via WebDAV, a .part file extension is used so
that the file isn't cached before the upload has been completed. .part files
are not compatible with files_encrytion's key management system however, so
we have to always sanitise such paths manually before using them.

View File

@ -0,0 +1,10 @@
/* Copyright (c) 2013, Sam Tuke, <samtuke@owncloud.com>
This file is licensed under the Affero General Public License version 3 or later.
See the COPYING-README file. */
#encryptAllError
, #encryptAllSuccess
, #recoveryEnabledError
, #recoveryEnabledSuccess {
display: none;
}

View File

@ -23,10 +23,11 @@
namespace OCA\Encryption;
use OC\Files\Filesystem;
/**
* Class for hook specific logic
*/
class Hooks {
// TODO: use passphrase for encrypting private key that is separate to
@ -40,152 +41,471 @@ class Hooks {
// Manually initialise Filesystem{} singleton with correct
// fake root path, in order to avoid fatal webdav errors
\OC\Files\Filesystem::init( $params['uid'], $params['uid'] . '/' . 'files' . '/' );
// NOTE: disabled because this give errors on webdav!
//\OC\Files\Filesystem::init( $params['uid'], '/' . 'files' . '/' );
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $params['uid'] );
// Check files_encryption infrastructure is ready for action
if ( ! $util->ready() ) {
\OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
return $util->setupServerSide( $params['password'] );
}
\OC_FileProxy::$enabled = false;
// setup user, if user not ready force relogin
if(Helper::setupUser($util, $params['password']) === false) {
return false;
}
$encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] );
\OC_FileProxy::$enabled = true;
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] );
$session = new Session();
$session = new Session( $view );
$session->setPrivateKey( $privateKey, $params['uid'] );
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
// Check if first-run file migration has already been performed
$migrationCompleted = $util->getMigrationStatus();
// Set legacy encryption key if it exists, to support
// depreciated encryption system
if (
$view1->file_exists( 'encryption.key' )
&& $encLegacyKey = $view1->file_get_contents( 'encryption.key' )
) {
// If migration not yet done
if ( ! $migrationCompleted ) {
$plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
$userView = new \OC_FilesystemView( '/' . $params['uid'] );
$session->setLegacyKey( $plainLegacyKey );
}
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
// Encrypt existing user files:
// This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt)
if (
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) {
// Set legacy encryption key if it exists, to support
// depreciated encryption system
if (
$userView->file_exists( 'encryption.key' )
&& $encLegacyKey = $userView->file_get_contents( 'encryption.key' )
) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login'
, \OC_Log::INFO
);
$plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
$session->setLegacyKey( $plainLegacyKey );
}
$publicKey = Keymanager::getPublicKey( $view, $params['uid'] );
// Encrypt existing user files:
// This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt)
if (
$util->encryptAll( '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO
);
}
// Register successful migration in DB
$util->setMigrationStatus( 1 );
}
return true;
}
/**
/**
* @brief setup encryption backend upon user created
* @note This method should never be called for users using client side encryption
*/
public static function postCreateUser( $params ) {
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $params['uid'] );
Helper::setupUser($util, $params['password']);
}
/**
* @brief cleanup encryption backend upon user deleted
* @note This method should never be called for users using client side encryption
*/
public static function postDeleteUser( $params ) {
$view = new \OC_FilesystemView( '/' );
// cleanup public key
$publicKey = '/public-keys/' . $params['uid'] . '.public.key';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view->unlink($publicKey);
\OC_FileProxy::$enabled = $proxyStatus;
}
/**
* @brief Change a user's encryption passphrase
* @param array $params keys: uid, password
*/
public static function setPassphrase( $params ) {
public static function setPassphrase($params) {
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// the necessary keys)
if ( Crypt::mode() == 'server' ) {
$session = new Session();
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
// Encrypt private key with new user pwd as passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $privateKey, $params['password'] );
// Save private key
Keymanager::setPrivateKey( $encryptedPrivateKey );
// NOTE: Session does not need to be updated as the
// private key has not changed, only the passphrase
// used to decrypt it has changed
if (Crypt::mode() == 'server') {
if ($params['uid'] == \OCP\User::getUser()) {
$view = new \OC_FilesystemView('/');
$session = new Session($view);
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
// Encrypt private key with new user pwd as passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']);
// Save private key
Keymanager::setPrivateKey($encryptedPrivateKey);
// NOTE: Session does not need to be updated as the
// private key has not changed, only the passphrase
// used to decrypt it has changed
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
$user = $params['uid'];
$recoveryPassword = $params['recoveryPassword'];
$newUserPassword = $params['password'];
$view = new \OC_FilesystemView('/');
// make sure that the users home is mounted
\OC\Files\Filesystem::initMountPoints($user);
$keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Save public key
$view->file_put_contents( '/public-keys/'.$user.'.public.key', $keypair['publicKey'] );
// Encrypt private key empty passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $newUserPassword );
// Save private key
$view->file_put_contents( '/'.$user.'/files_encryption/'.$user.'.private.key', $encryptedPrivateKey );
if ( $recoveryPassword ) { // if recovery key is set we can re-encrypt the key files
$util = new Util($view, $user);
$util->recoverUsersFiles($recoveryPassword);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}
}
/*
* @brief check if files can be encrypted to every user.
*/
/**
* @param $params
*/
public static function preShared($params) {
$users = array();
$view = new \OC\Files\View('/public-keys/');
switch ($params['shareType']) {
case \OCP\Share::SHARE_TYPE_USER:
$users[] = $params['shareWith'];
break;
case \OCP\Share::SHARE_TYPE_GROUP:
$users = \OC_Group::usersInGroup($params['shareWith']);
break;
}
$error = false;
foreach ($users as $user) {
if (!$view->file_exists($user . '.public.key')) {
$error = true;
break;
}
}
if($error)
// Set flag var 'run' to notify emitting
// script that hook execution failed
$params['run']->run = false;
// TODO: Make sure files_sharing provides user
// feedback on failed share
}
/**
* @brief
*/
public static function postShared($params) {
// NOTE: $params has keys:
// [itemType] => file
// itemSource -> int, filecache file ID
// [parent] =>
// [itemTarget] => /13
// shareWith -> string, uid of user being shared to
// fileTarget -> path of file being shared
// uidOwner -> owner of the original file being shared
// [shareType] => 0
// [shareWith] => test1
// [uidOwner] => admin
// [permissions] => 17
// [fileSource] => 13
// [fileTarget] => /test8
// [id] => 10
// [token] =>
// [run] => whether emitting script should continue to run
// TODO: Should other kinds of item be encrypted too?
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$view = new \OC_FilesystemView('/');
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util($view, $userId);
$path = $util->fileIdToPath($params['itemSource']);
$share = $util->getParentFromShare($params['id']);
//if parent is set, then this is a re-share action
if ($share['parent'] != null) {
// get the parent from current share
$parent = $util->getShareParent($params['parent']);
// if parent is file the it is an 1:1 share
if ($parent['item_type'] === 'file') {
// prefix path with Shared
$path = '/Shared' . $parent['file_target'];
} else {
// NOTE: parent is folder but shared was a file!
// we try to rebuild the missing path
// some examples we face here
// user1 share folder1 with user2 folder1 has
// the following structure
// /folder1/subfolder1/subsubfolder1/somefile.txt
// user2 re-share subfolder2 with user3
// user3 re-share somefile.txt user4
// so our path should be
// /Shared/subfolder1/subsubfolder1/somefile.txt
// while user3 is sharing
if ($params['itemType'] === 'file') {
// get target path
$targetPath = $util->fileIdToPath($params['fileSource']);
$targetPathSplit = array_reverse(explode('/', $targetPath));
// init values
$path = '';
$sharedPart = ltrim($parent['file_target'], '/');
// rebuild path
foreach ($targetPathSplit as $pathPart) {
if ($pathPart !== $sharedPart) {
$path = '/' . $pathPart . $path;
} else {
break;
}
}
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $path;
} else {
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $params['fileTarget'];
}
}
}
$sharingEnabled = \OCP\Share::isEnabled();
// if a folder was shared, get a list of all (sub-)folders
if ($params['itemType'] === 'folder') {
$allFiles = $util->getAllFiles($path);
} else {
$allFiles = array($path);
}
foreach ($allFiles as $path) {
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
$util->setSharedFileKeyfiles( $session, $usersSharing, $path );
}
}
}
/**
* @brief update the encryption key of the file uploaded by the client
* @brief
*/
public static function updateKeyfile( $params ) {
if ( Crypt::mode() == 'client' ) {
public static function postUnshare( $params ) {
// NOTE: $params has keys:
// [itemType] => file
// [itemSource] => 13
// [shareType] => 0
// [shareWith] => test1
// [itemParent] =>
if ( $params['itemType'] === 'file' || $params['itemType'] === 'folder' ) {
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId);
$path = $util->fileIdToPath( $params['itemSource'] );
// check if this is a re-share
if ( $params['itemParent'] ) {
// get the parent from current share
$parent = $util->getShareParent( $params['itemParent'] );
// get target path
$targetPath = $util->fileIdToPath( $params['itemSource'] );
$targetPathSplit = array_reverse( explode( '/', $targetPath ) );
// init values
$path = '';
$sharedPart = ltrim( $parent['file_target'], '/' );
// rebuild path
foreach ( $targetPathSplit as $pathPart ) {
if ( $pathPart !== $sharedPart ) {
$path = '/' . $pathPart . $path;
} else {
break;
}
}
// prefix path with Shared
$path = '/Shared' . $parent['file_target'] . $path;
}
// for group shares get a list of the group members
if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_GROUP ) {
$userIds = \OC_Group::usersInGroup($params['shareWith']);
} else if ( $params['shareType'] == \OCP\Share::SHARE_TYPE_LINK ){
$userIds = array( $util->getPublicShareKeyId() );
} else {
$userIds = array( $params['shareWith'] );
}
// if we unshare a folder we need a list of all (sub-)files
if ( $params['itemType'] === 'folder' ) {
if ( isset( $params['properties']['key'] ) ) {
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\User::getUser();
Keymanager::setFileKey( $view, $params['path'], $userId, $params['properties']['key'] );
$allFiles = $util->getAllFiles( $path );
} else {
\OC_Log::write(
'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!"
, \OC_Log::ERROR
);
error_log( "Client side encryption is enabled but the client doesn't provide an encryption key for the file!" );
}
$allFiles = array( $path );
}
foreach ( $allFiles as $path ) {
// check if the user still has access to the file, otherwise delete share key
$sharingUsers = $util->getSharingUsersArray( true, $path );
// Unshare every user who no longer has access to the file
$delUsers = array_diff( $userIds, $sharingUsers);
// delete share key
Keymanager::delShareKey( $view, $delUsers, $path );
}
}
}
/**
* @brief
*/
public static function postShared( $params ) {
}
/**
* @brief
*/
public static function preUnshare( $params ) {
// Delete existing catfile
// Generate new catfile and env keys
// Save env keys to user folders
}
/**
* @brief
*/
public static function preUnshareAll( $params ) {
trigger_error( "preUnshareAll" );
}
* @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
* @param array with oldpath and newpath
*
* This function is connected to the rename signal of OC_Filesystem and adjust the name and location
* of the stored versions along the actual file
*/
public static function postRename($params) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/');
$session = new Session($view);
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
// Format paths to be relative to user files dir
$oldKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']);
$newKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']);
// add key ext if this is not an folder
if (!$view->is_dir($oldKeyfilePath)) {
$oldKeyfilePath .= '.key';
$newKeyfilePath .= '.key';
// handle share-keys
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$params['oldpath']);
$matches = glob(preg_quote($localKeyPath).'*.shareKey');
foreach ($matches as $src) {
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
// create destination folder if not exists
if(!file_exists(dirname($dst))) {
mkdir(dirname($dst), 0750, true);
}
rename($src, $dst);
}
} else {
// handle share-keys folders
$oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']);
$newShareKeyfilePath = \OC\Files\Filesystem::normalizePath($userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']);
// create destination folder if not exists
if(!$view->file_exists(dirname($newShareKeyfilePath))) {
$view->mkdir(dirname($newShareKeyfilePath), 0750, true);
}
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
}
// Rename keyfile so it isn't orphaned
if($view->file_exists($oldKeyfilePath)) {
// create destination folder if not exists
if(!$view->file_exists(dirname($newKeyfilePath))) {
$view->mkdir(dirname($newKeyfilePath), 0750, true);
}
$view->rename($oldKeyfilePath, $newKeyfilePath);
}
// build the path to the file
$newPath = '/' . $userId . '/files' .$params['newpath'];
$newPathRelative = $params['newpath'];
if($util->fixFileSize($newPath)) {
// get sharing app state
$sharingEnabled = \OCP\Share::isEnabled();
// get users
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
// update sharing-keys
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}

View File

@ -0,0 +1,102 @@
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>, Robin Appelman
* <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OC.msg={
startSaving:function(selector){
$(selector)
.html( t('settings', 'Saving...') )
.removeClass('success')
.removeClass('error')
.stop(true, true)
.show();
},
finishedSaving:function(selector, data){
if( data.status === "success" ){
$(selector).html( data.data.message )
.addClass('success')
.stop(true, true)
.delay(3000)
.fadeOut(900);
}else{
$(selector).html( data.data.message ).addClass('error');
}
}
};
$(document).ready(function(){
// Trigger ajax on recoveryAdmin status change
var enabledStatus = $('#adminEnableRecovery').val();
$('input:password[name="recoveryPassword"]').keyup(function(event) {
var recoveryPassword = $( '#recoveryPassword' ).val();
var checkedButton = $('input:radio[name="adminEnableRecovery"]:checked').val();
var uncheckedValue = (1+parseInt(checkedButton)) % 2;
if (recoveryPassword != '' ) {
$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').removeAttr("disabled");
} else {
$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').attr("disabled", "true");
}
});
$( 'input:radio[name="adminEnableRecovery"]' ).change(
function() {
var recoveryStatus = $( this ).val();
var oldStatus = (1+parseInt(recoveryStatus)) % 2;
var recoveryPassword = $( '#recoveryPassword' ).val();
$.post(
OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' )
, { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword }
, function( result ) {
if (result.status === "error") {
OC.Notification.show(t('admin', result.data.message));
$('input:radio[name="adminEnableRecovery"][value="'+oldStatus.toString()+'"]').attr("checked", "true");
} else {
OC.Notification.hide();
if (recoveryStatus === "0") {
$('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true");
$('input:password[name="changeRecoveryPassword"]').attr("disabled", "true");
$('input:password[name="changeRecoveryPassword"]').val("");
} else {
$('input:password[name="changeRecoveryPassword"]').removeAttr("disabled");
}
}
}
);
}
);
// change recovery password
$('input:password[name="changeRecoveryPassword"]').keyup(function(event) {
var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val();
if (newRecoveryPassword != '' && oldRecoveryPassword != '' ) {
$('button:button[name="submitChangeRecoveryKey"]').removeAttr("disabled");
} else {
$('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true");
}
});
$('button:button[name="submitChangeRecoveryKey"]').click(function() {
var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val();
OC.msg.startSaving('#encryption .msg');
$.post(
OC.filePath( 'files_encryption', 'ajax', 'changeRecoveryPassword.php' )
, { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword }
, function( data ) {
if (data.status == "error") {
OC.msg.finishedSaving('#encryption .msg', data);
} else {
OC.msg.finishedSaving('#encryption .msg', data);
}
}
);
});
});

View File

@ -0,0 +1,60 @@
/**
* Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
$(document).ready(function(){
// Trigger ajax on recoveryAdmin status change
$( 'input:radio[name="userEnableRecovery"]' ).change(
function() {
// Hide feedback messages in case they're already visible
$('#recoveryEnabledSuccess').hide();
$('#recoveryEnabledError').hide();
var recoveryStatus = $( this ).val();
$.post(
OC.filePath( 'files_encryption', 'ajax', 'userrecovery.php' )
, { userEnableRecovery: recoveryStatus }
, function( data ) {
if ( data.status == "success" ) {
$('#recoveryEnabledSuccess').show();
} else {
$('#recoveryEnabledError').show();
}
}
);
// Ensure page is not reloaded on form submit
return false;
}
);
$("#encryptAll").click(
function(){
// Hide feedback messages in case they're already visible
$('#encryptAllSuccess').hide();
$('#encryptAllError').hide();
var userPassword = $( '#userPassword' ).val();
var encryptAll = $( '#encryptAll' ).val();
$.post(
OC.filePath( 'files_encryption', 'ajax', 'encryptall.php' )
, { encryptAll: encryptAll, userPassword: userPassword }
, function( data ) {
if ( data.status == "success" ) {
$('#encryptAllSuccess').show();
} else {
$('#encryptAllError').show();
}
}
);
// Ensure page is not reloaded on form submit
return false;
}
);
});

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
$(document).ready(function(){
$('#encryption_blacklist').multiSelect({
oncheck:blackListChange,
onuncheck:blackListChange,
createText:'...'
});
function blackListChange(){
var blackList=$('#encryption_blacklist').val().join(',');
OC.AppConfig.setValue('files_encryption','type_blacklist',blackList);
}
})

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert",
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:",
"None" => "Nichts"
"None" => "Keine"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert",
"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:",
"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:",
"None" => "Nichts"
"None" => "Keine"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.",
"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:",
"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:",
"None" => "Τίποτα"
"None" => "Καμία"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.",
"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:",
"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:",
"None" => "Ezer"
"None" => "Bat ere ez"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "La cifratura dei file è abilitata.",
"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:",
"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:",
"None" => "Nessuno"
"None" => "Nessuna"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Szyfrowanie plików jest włączone",
"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:",
"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:",
"None" => "Nic"
"None" => "Brak"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "A criptografia de arquivos está ativada.",
"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:",
"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:",
"None" => "Nada"
"None" => "Nenhuma"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Шифрование файла включено.",
"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:",
"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:",
"None" => "Нет новостей"
"None" => "Ничего"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Šifrovanie súborov nastavené.",
"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:",
"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov",
"None" => "Žiadny"
"None" => "Žiadne"
);

View File

@ -1,4 +1,4 @@
<?php $TRANSLATIONS = array(
"Encryption" => "การเข้ารหัส",
"None" => "ไม่มี"
"None" => "ไม่ต้อง"
);

View File

@ -3,5 +3,5 @@
"File encryption is enabled." => "Mã hóa file đã mở",
"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa",
"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau",
"None" => "Không gì cả"
"None" => "Không có gì hết"
);

View File

@ -25,23 +25,19 @@
namespace OCA\Encryption;
require_once 'Crypt_Blowfish/Blowfish.php';
// Todo:
// - Add a setting "Don´t encrypt files larger than xx because of performance"
// - Don't use a password directly as encryption key. but a key which is
// stored on the server and encrypted with the user password. -> change pass
// faster
//require_once '../3rdparty/Crypt_Blowfish/Blowfish.php';
require_once realpath( dirname( __FILE__ ) . '/../3rdparty/Crypt_Blowfish/Blowfish.php' );
/**
* Class for common cryptography functionality
*/
class Crypt {
class Crypt
{
/**
* @brief return encryption mode client or server side encryption
* @param string user name (use system wide setting if name=null)
* @param string $user name (use system wide setting if name=null)
* @return string 'client' or 'server'
*/
public static function mode( $user = null ) {
@ -56,7 +52,7 @@ class Crypt {
*/
public static function createKeypair() {
$res = openssl_pkey_new();
$res = openssl_pkey_new( array( 'private_key_bits' => 4096 ) );
// Get private key
openssl_pkey_export( $res, $privateKey );
@ -66,14 +62,14 @@ class Crypt {
$publicKey = $publicKey['key'];
return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) );
return ( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) );
}
/**
* @brief Add arbitrary padding to encrypted data
* @param string $data data to be padded
* @return padded data
* @return string padded data
* @note In order to end up with data exactly 8192 bytes long we must
* add two letters. It is impossible to achieve exactly 8192 length
* blocks with encryption alone, hence padding is added to achieve the
@ -90,7 +86,7 @@ class Crypt {
/**
* @brief Remove arbitrary padding to encrypted data
* @param string $padded padded data to remove padding from
* @return unpadded data on success, false on error
* @return string unpadded data on success, false on error
*/
public static function removePadding( $padded ) {
@ -111,10 +107,11 @@ class Crypt {
/**
* @brief Check if a file's contents contains an IV and is symmetrically encrypted
* @return true / false
* @param $content
* @return boolean
* @note see also OCA\Encryption\Util->isEncryptedPath()
*/
public static function isCatfile( $content ) {
public static function isCatfileContent( $content ) {
if ( !$content ) {
@ -133,7 +130,7 @@ class Crypt {
// Fetch identifier from start of metadata
$identifier = substr( $meta, 0, 6 );
if ( $identifier == '00iv00') {
if ( $identifier == '00iv00' ) {
return true;
@ -155,7 +152,7 @@ class Crypt {
// TODO: Use DI to get \OC\Files\Filesystem out of here
// Fetch all file metadata from DB
$metadata = \OC\Files\Filesystem::getFileInfo( $path, '' );
$metadata = \OC\Files\Filesystem::getFileInfo( $path );
// Return encryption status
return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted'];
@ -164,9 +161,10 @@ class Crypt {
/**
* @brief Check if a file is encrypted via legacy system
* @param $data
* @param string $relPath The path of the file, relative to user/data;
* e.g. filename or /Docs/filename, NOT admin/files/filename
* @return true / false
* @return boolean
*/
public static function isLegacyEncryptedContent( $data, $relPath ) {
@ -179,7 +177,7 @@ class Crypt {
if (
isset( $metadata['encrypted'] )
and $metadata['encrypted'] === true
and ! self::isCatfile( $data )
and !self::isCatfileContent( $data )
) {
return true;
@ -194,7 +192,10 @@ class Crypt {
/**
* @brief Symmetrically encrypt a string
* @returns encrypted file
* @param $plainContent
* @param $iv
* @param string $passphrase
* @return string encrypted file content
*/
public static function encrypt( $plainContent, $iv, $passphrase = '' ) {
@ -214,7 +215,11 @@ class Crypt {
/**
* @brief Symmetrically decrypt a string
* @returns decrypted file
* @param $encryptedContent
* @param $iv
* @param $passphrase
* @throws \Exception
* @return string decrypted file content
*/
public static function decrypt( $encryptedContent, $iv, $passphrase ) {
@ -222,7 +227,6 @@ class Crypt {
return $plainContent;
} else {
throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' );
@ -237,7 +241,7 @@ class Crypt {
* @param string $iv IV to be concatenated
* @returns string concatenated content
*/
public static function concatIv ( $content, $iv ) {
public static function concatIv( $content, $iv ) {
$combined = $content . '00iv00' . $iv;
@ -250,7 +254,7 @@ class Crypt {
* @param string $catFile concatenated data to be split
* @returns array keys: encrypted, iv
*/
public static function splitIv ( $catFile ) {
public static function splitIv( $catFile ) {
// Fetch encryption metadata from end of file
$meta = substr( $catFile, -22 );
@ -272,8 +276,10 @@ class Crypt {
/**
* @brief Symmetrically encrypts a string and returns keyfile content
* @param $plainContent content to be encrypted in keyfile
* @returns encrypted content combined with IV
* @param string $plainContent content to be encrypted in keyfile
* @param string $passphrase
* @return bool|string
* @return string encrypted content combined with IV
* @note IV need not be specified, as it will be stored in the returned keyfile
* and remain accessible therein.
*/
@ -309,10 +315,14 @@ class Crypt {
/**
* @brief Symmetrically decrypts keyfile content
* @param string $source
* @param string $target
* @param string $key the decryption key
* @returns decrypted content
* @param $keyfileContent
* @param string $passphrase
* @throws \Exception
* @return bool|string
* @internal param string $source
* @internal param string $target
* @internal param string $key the decryption key
* @returns string decrypted content
*
* This function decrypts a file
*/
@ -334,6 +344,8 @@ class Crypt {
return $plainContent;
} else {
return false;
}
}
@ -350,11 +362,11 @@ class Crypt {
$key = self::generateKey();
if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) {
if ( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) {
return array(
'key' => $key
, 'encrypted' => $encryptedContent
'key' => $key,
'encrypted' => $encryptedContent
);
} else {
@ -368,22 +380,41 @@ class Crypt {
/**
* @brief Create asymmetrically encrypted keyfile content using a generated key
* @param string $plainContent content to be encrypted
* @returns array keys: key, encrypted
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
*
* This function decrypts a file
* @param array $publicKeys array keys must be the userId of corresponding user
* @returns array keys: keys (array, key = userId), data
* @note symmetricDecryptFileContent() can decrypt files created using this method
*/
public static function multiKeyEncrypt( $plainContent, array $publicKeys ) {
// openssl_seal returns false without errors if $plainContent
// is empty, so trigger our own error
if ( empty( $plainContent ) ) {
throw new \Exception( 'Cannot mutliKeyEncrypt empty plain content' );
}
// Set empty vars to be set by openssl by reference
$sealed = '';
$envKeys = array();
$shareKeys = array();
$mappedShareKeys = array();
if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) {
if ( openssl_seal( $plainContent, $sealed, $shareKeys, $publicKeys ) ) {
$i = 0;
// Ensure each shareKey is labelled with its
// corresponding userId
foreach ( $publicKeys as $userId => $publicKey ) {
$mappedShareKeys[$userId] = $shareKeys[$i];
$i++;
}
return array(
'keys' => $envKeys
, 'encrypted' => $sealed
'keys' => $mappedShareKeys,
'data' => $sealed
);
} else {
@ -396,13 +427,17 @@ class Crypt {
/**
* @brief Asymmetrically encrypt a file using multiple public keys
* @param string $plainContent content to be encrypted
* @param $encryptedContent
* @param $shareKey
* @param $privateKey
* @return bool
* @internal param string $plainContent content to be encrypted
* @returns string $plainContent decrypted string
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
*
* This function decrypts a file
*/
public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) {
public static function multiKeyDecrypt( $encryptedContent, $shareKey, $privateKey ) {
if ( !$encryptedContent ) {
@ -410,7 +445,7 @@ class Crypt {
}
if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) {
if ( openssl_open( $encryptedContent, $plainContent, $shareKey, $privateKey ) ) {
return $plainContent;
@ -425,8 +460,8 @@ class Crypt {
}
/**
* @brief Asymmetrically encrypt a string using a public key
* @returns encrypted file
* @brief Asymetrically encrypt a string using a public key
* @return string encrypted file
*/
public static function keyEncrypt( $plainContent, $publicKey ) {
@ -438,110 +473,17 @@ class Crypt {
/**
* @brief Asymetrically decrypt a file using a private key
* @returns decrypted file
* @return string decrypted file
*/
public static function keyDecrypt( $encryptedContent, $privatekey ) {
openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
return $plainContent;
}
/**
* @brief Encrypts content symmetrically and generates keyfile asymmetrically
* @returns array containing catfile and new keyfile.
* keys: data, key
* @note this method is a wrapper for combining other crypt class methods
*/
public static function keyEncryptKeyfile( $plainContent, $publicKey ) {
// Encrypt plain data, generate keyfile & encrypted file
$cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent );
// Encrypt keyfile
$cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey );
return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey );
}
/**
* @brief Takes catfile, keyfile, and private key, and
* performs decryption
* @returns decrypted content
* @note this method is a wrapper for combining other crypt class methods
*/
public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) {
// Decrypt the keyfile with the user's private key
$decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey );
// Decrypt the catfile symmetrically using the decrypted keyfile
$decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile );
return $decryptedData;
}
/**
* @brief Symmetrically encrypt a file by combining encrypted component data blocks
*/
public static function symmetricBlockEncryptFileContent( $plainContent, $key ) {
$crypted = '';
$remaining = $plainContent;
$testarray = array();
while( strlen( $remaining ) ) {
//echo "\n\n\$block = ".substr( $remaining, 0, 6126 );
// Encrypt a chunk of unencrypted data and add it to the rest
$block = self::symmetricEncryptFileContent( substr( $remaining, 0, 6126 ), $key );
$padded = self::addPadding( $block );
$crypted .= $block;
$testarray[] = $block;
// Remove the data already encrypted from remaining unencrypted data
$remaining = substr( $remaining, 6126 );
$result = @openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
if ( $result ) {
return $plainContent;
}
return $crypted;
}
/**
* @brief Symmetrically decrypt a file by combining encrypted component data blocks
*/
public static function symmetricBlockDecryptFileContent( $crypted, $key ) {
$decrypted = '';
$remaining = $crypted;
$testarray = array();
while( strlen( $remaining ) ) {
$testarray[] = substr( $remaining, 0, 8192 );
// Decrypt a chunk of unencrypted data and add it to the rest
$decrypted .= self::symmetricDecryptFileContent( $remaining, $key );
// Remove the data already encrypted from remaining unencrypted data
$remaining = substr( $remaining, 8192 );
}
return $decrypted;
return $result;
}
@ -586,7 +528,7 @@ class Crypt {
if ( !$strong ) {
// If OpenSSL indicates randomness is insecure, log error
throw new \Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' );
throw new \Exception( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' );
}
@ -621,6 +563,10 @@ class Crypt {
}
/**
* @param $passphrase
* @return mixed
*/
public static function legacyCreateKey( $passphrase ) {
// Generate a random integer
@ -635,9 +581,11 @@ class Crypt {
/**
* @brief encrypts content using legacy blowfish system
* @param $content the cleartext message you want to encrypt
* @param $key the encryption key (optional)
* @returns encrypted content
* @param string $content the cleartext message you want to encrypt
* @param string $passphrase
* @return
* @internal param \OCA\Encryption\the $key encryption key (optional)
* @returns string encrypted content
*
* This function encrypts an content
*/
@ -651,9 +599,11 @@ class Crypt {
/**
* @brief decrypts content using legacy blowfish system
* @param $content the cleartext message you want to decrypt
* @param $key the encryption key (optional)
* @returns cleartext content
* @param string $content the cleartext message you want to decrypt
* @param string $passphrase
* @return string
* @internal param \OCA\Encryption\the $key encryption key (optional)
* @return string cleartext content
*
* This function decrypts an content
*/
@ -663,33 +613,49 @@ class Crypt {
$decrypted = $bf->decrypt( $content );
$trimmed = rtrim( $decrypted, "\0" );
return $trimmed;
}
public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) {
$decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase );
$recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey );
return $recrypted;
return rtrim( $decrypted, "\0" );;
}
/**
* @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV
* @param $legacyContent the legacy encrypted content to re-encrypt
* @returns cleartext content
*
* This function decrypts an content
* @param $data
* @param string $key
* @param int $maxLength
* @return string
*/
public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) {
private static function legacyBlockDecrypt( $data, $key = '', $maxLength = 0 ) {
$result = '';
while ( strlen( $data ) ) {
$result .= self::legacyDecrypt( substr( $data, 0, 8192 ), $key );
$data = substr( $data, 8192 );
}
if ( $maxLength > 0 ) {
return substr( $result, 0, $maxLength );
} else {
return rtrim( $result, "\0" );
}
}
// TODO: write me
/**
* @param $legacyEncryptedContent
* @param $legacyPassphrase
* @param $publicKeys
* @param $newPassphrase
* @param $path
* @return array
*/
public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKeys, $newPassphrase, $path ) {
$decrypted = self::legacyBlockDecrypt( $legacyEncryptedContent, $legacyPassphrase );
// Encrypt plain data, generate keyfile & encrypted file
$cryptedData = self::symmetricEncryptFileContentKeyfile( $decrypted );
// Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $cryptedData['key'], $publicKeys );
return array( 'data' => $cryptedData['encrypted'], 'filekey' => $multiEncrypted['data'], 'sharekeys' => $multiEncrypted['keys'] );
}
}
}

View File

@ -0,0 +1,176 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
namespace OCA\Encryption;
/**
* @brief Class to manage registration of hooks an various helper methods
*/
/**
* Class Helper
* @package OCA\Encryption
*/
class Helper
{
/**
* @brief register share related hooks
*
*/
public static function registerShareHooks() {
\OCP\Util::connectHook( 'OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared' );
\OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' );
\OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' );
}
/**
* @brief register user related hooks
*
*/
public static function registerUserHooks() {
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' );
\OCP\Util::connectHook( 'OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' );
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' );
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' );
}
/**
* @brief register filesystem related hooks
*
*/
public static function registerFilesystemHooks() {
\OCP\Util::connectHook( 'OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename' );
}
/**
* @brief setup user for files_encryption
*
* @param Util $util
* @param string $password
* @return bool
*/
public static function setupUser( $util, $password ) {
// Check files_encryption infrastructure is ready for action
if ( !$util->ready() ) {
\OC_Log::write( 'Encryption library', 'User account "' . $util->getUserId() . '" is not ready for encryption; configuration started', \OC_Log::DEBUG );
if ( !$util->setupServerSide( $password ) ) {
return false;
}
}
return true;
}
/**
* @brief enable recovery
*
* @param $recoveryKeyId
* @param $recoveryPassword
* @internal param \OCA\Encryption\Util $util
* @internal param string $password
* @return bool
*/
public static function adminEnableRecovery( $recoveryKeyId, $recoveryPassword ) {
$view = new \OC\Files\View( '/' );
if ( $recoveryKeyId === null ) {
$recoveryKeyId = 'recovery_' . substr( md5( time() ), 0, 8 );
\OC_Appconfig::setValue( 'files_encryption', 'recoveryKeyId', $recoveryKeyId );
}
if ( !$view->is_dir( '/owncloud_private_key' ) ) {
$view->mkdir( '/owncloud_private_key' );
}
if (
( !$view->file_exists( "/public-keys/" . $recoveryKeyId . ".public.key" )
|| !$view->file_exists( "/owncloud_private_key/" . $recoveryKeyId . ".private.key" ) )
) {
$keypair = \OCA\Encryption\Crypt::createKeypair();
\OC_FileProxy::$enabled = false;
// Save public key
if ( !$view->is_dir( '/public-keys' ) ) {
$view->mkdir( '/public-keys' );
}
$view->file_put_contents( '/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey'] );
// Encrypt private key empthy passphrase
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $recoveryPassword );
// Save private key
$view->file_put_contents( '/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey );
// create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt( "ownCloud", $keypair['publicKey'] );
if ( !$view->is_dir( '/control-file' ) ) {
$view->mkdir( '/control-file' );
}
$view->file_put_contents( '/control-file/controlfile.enc', $encryptedControlData );
\OC_FileProxy::$enabled = true;
// Set recoveryAdmin as enabled
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 1 );
$return = true;
} else { // get recovery key and check the password
$util = new \OCA\Encryption\Util( new \OC_FilesystemView( '/' ), \OCP\User::getUser() );
$return = $util->checkRecoveryPassword( $recoveryPassword );
if ( $return ) {
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 1 );
}
}
return $return;
}
/**
* @brief disable recovery
*
* @param $recoveryPassword
* @return bool
*/
public static function adminDisableRecovery( $recoveryPassword ) {
$util = new Util( new \OC_FilesystemView( '/' ), \OCP\User::getUser() );
$return = $util->checkRecoveryPassword( $recoveryPassword );
if ( $return ) {
// Set recoveryAdmin as disabled
\OC_Appconfig::setValue( 'files_encryption', 'recoveryAdminEnabled', 0 );
}
return $return;
}
}

View File

@ -27,20 +27,28 @@ namespace OCA\Encryption;
* @brief Class to manage storage and retrieval of encryption keys
* @note Where a method requires a view object, it's root must be '/'
*/
class Keymanager {
class Keymanager
{
/**
* @brief retrieve the ENCRYPTED private key from a user
*
* @return string private key or false
*
* @param \OC_FilesystemView $view
* @param string $user
* @return string private key or false (hopefully)
* @note the key returned by this method must be decrypted before use
*/
public static function getPrivateKey( \OC_FilesystemView $view, $user ) {
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key';
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$key = $view->file_get_contents( $path );
\OC_FileProxy::$enabled = $proxyStatus;
return $key;
}
@ -51,101 +59,150 @@ class Keymanager {
* @return string public key or false
*/
public static function getPublicKey( \OC_FilesystemView $view, $userId ) {
return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' );
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$result = $view->file_get_contents( '/public-keys/' . $userId . '.public.key' );
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief retrieve both keys from a user (private and public)
* @brief Retrieve a user's public and private key
* @param \OC_FilesystemView $view
* @param $userId
* @return array keys: privateKey, publicKey
*/
public static function getUserKeys( \OC_FilesystemView $view, $userId ) {
return array(
'publicKey' => self::getPublicKey( $view, $userId )
, 'privateKey' => self::getPrivateKey( $view, $userId )
, 'privateKey' => self::getPrivateKey( $view, $userId )
);
}
/**
* @brief Retrieve public keys of all users with access to a file
* @param string $path Path to file
* @return array of public keys for the given file
* @note Checks that the sharing app is enabled should be performed
* by client code, that isn't checked here
*/
public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) {
$path = ltrim( $path, '/' );
$filepath = '/' . $userId . '/files/' . $filePath;
// Check if sharing is enabled
if ( OC_App::isEnabled( 'files_sharing' ) ) {
} else {
// check if it is a file owned by the user and not shared at all
$userview = new \OC_FilesystemView( '/'.$userId.'/files/' );
if ( $userview->file_exists( $path ) ) {
$users[] = $userId;
}
}
$view = new \OC_FilesystemView( '/public-keys/' );
$keylist = array();
$count = 0;
foreach ( $users as $user ) {
$keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' );
}
return $keylist;
}
/**
* @brief Retrieve public keys for given users
* @param \OC_FilesystemView $view
* @param array $userIds
* @return array of public keys for the specified users
*/
public static function getPublicKeys( \OC_FilesystemView $view, array $userIds ) {
$keys = array();
foreach ( $userIds as $userId ) {
$keys[$userId] = self::getPublicKey( $view, $userId );
}
return $keys;
}
/**
* @brief store file encryption key
*
* @param \OC_FilesystemView $view
* @param string $path relative path of the file, including filename
* @param string $key
* @param $userId
* @param $catfile
* @internal param string $key
* @return bool true/false
* @note The keyfile is not encrypted here. Client code must
* @note The keyfile is not encrypted here. Client code must
* asymmetrically encrypt the keyfile before passing it to this method
*/
public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) {
$basePath = '/' . $userId . '/files_encryption/keyfiles';
$targetPath = self::keySetPreparation( $view, $path, $basePath, $userId );
if ( $view->is_dir( $basePath . '/' . $targetPath ) ) {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $path );
$basePath = '/' . $owner . '/files_encryption/keyfiles';
$targetPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
if ( !$view->is_dir( $basePath . '/' . $targetPath ) ) {
// create all parent folders
$info = pathinfo( $basePath . '/' . $targetPath );
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] );
if ( !file_exists( $keyfileFolderName ) ) {
mkdir( $keyfileFolderName, 0750, true );
}
}
// try reusing key file if part file
if ( self::isPartialFilePath( $targetPath ) ) {
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile );
} else {
// Save the keyfile in parallel directory
return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief Remove .path extension from a file path
* @param string $path Path that may identify a .part file
* @return string File path without .part extension
* @note this is needed for reusing keys
*/
public static function fixPartialFilePath( $path ) {
if ( preg_match( '/\.part$/', $path ) ) {
$newLength = strlen( $path ) - 5;
$fPath = substr( $path, 0, $newLength );
return $fPath;
} else {
return $path;
}
}
/**
* @brief Check if a path is a .part file
* @param string $path Path that may identify a .part file
* @return bool
*/
public static function isPartialFilePath( $path ) {
if ( preg_match( '/\.part$/', $path ) ) {
return true;
} else {
return false;
}
}
/**
* @brief retrieve keyfile for an encrypted file
* @param \OC_FilesystemView $view
@ -157,27 +214,50 @@ class Keymanager {
* of the keyfile must be performed by client code
*/
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) {
$filePath_f = ltrim( $filePath, '/' );
$catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key';
if ( $view->file_exists( $catfilePath ) ) {
return $view->file_get_contents( $catfilePath );
} else {
return false;
// try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) {
$result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) );
if ( $result ) {
return $result;
}
}
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $filePath );
$filePath_f = ltrim( $filename, '/' );
$keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ( $view->file_exists( $keyfilePath ) ) {
$result = $view->file_get_contents( $keyfilePath );
} else {
$result = false;
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief Delete a keyfile
*
* @param OC_FilesystemView $view
* @param \OC_FilesystemView $view
* @param string $userId username
* @param string $path path of the file the key belongs to
* @return bool Outcome of unlink operation
@ -185,139 +265,299 @@ class Keymanager {
* /data/admin/files/mydoc.txt
*/
public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) {
$trimmed = ltrim( $path, '/' );
$keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key';
// Unlink doesn't tell us if file was deleted (not found returns
// true), so we perform our own test
if ( $view->file_exists( $keyPath ) ) {
return $view->unlink( $keyPath );
} else {
\OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR );
return false;
$keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed;
$result = false;
if ( $view->is_dir( $keyPath ) ) {
$result = $view->unlink( $keyPath );
} else if ( $view->file_exists( $keyPath . '.key' ) ) {
$result = $view->unlink( $keyPath . '.key' );
}
if ( !$result ) {
\OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR );
}
return $result;
}
/**
* @brief store private key from the user
* @param string key
* @param string $key
* @return bool
* @note Encryption of the private key must be performed by client code
* as no encryption takes place here
*/
public static function setPrivateKey( $key ) {
$user = \OCP\User::getUser();
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption' );
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) )
$view->mkdir( '' );
return $view->file_put_contents( $user . '.private.key', $key );
$result = $view->file_put_contents( $user . '.private.key', $key );
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief store private keys from the user
*
* @param string privatekey
* @param string publickey
* @return bool true/false
*/
public static function setUserKeys($privatekey, $publickey) {
return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) );
}
/**
* @brief store public key of the user
*
* @param string key
* @return bool true/false
*/
public static function setPublicKey( $key ) {
$view = new \OC_FilesystemView( '/public-keys' );
\OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) )
$view->mkdir( '' );
return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key );
}
/**
* @brief store file encryption key
* @brief store share key
*
* @param \OC_FilesystemView $view
* @param string $path relative path of the file, including filename
* @param string $key
* @param null $view
* @param string $dbClassName
* @param $userId
* @param $shareKey
* @internal param string $key
* @internal param string $dbClassName
* @return bool true/false
* @note The keyfile is not encrypted here. Client code must
* asymmetrically encrypt the keyfile before passing it to this method
*/
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) {
$basePath = '/' . $userId . '/files_encryption/share-keys';
$shareKeyPath = self::keySetPreparation( $view, $path, $basePath, $userId );
return $view->file_put_contents( $basePath . '/' . $shareKeyPath . '.shareKey', $shareKey );
// Here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $path );
$basePath = '/' . $owner . '/files_encryption/share-keys';
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner );
// try reusing key file if part file
if ( self::isPartialFilePath( $shareKeyPath ) ) {
$writePath = $basePath . '/' . self::fixPartialFilePath( $shareKeyPath ) . '.' . $userId . '.shareKey';
} else {
$writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey';
}
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$result = $view->file_put_contents( $writePath, $shareKey );
\OC_FileProxy::$enabled = $proxyStatus;
if (
is_int( $result )
&& $result > 0
) {
return true;
} else {
return false;
}
}
/**
* @brief store multiple share keys for a single file
* @param \OC_FilesystemView $view
* @param $path
* @param array $shareKeys
* @return bool
*/
public static function setShareKeys( \OC_FilesystemView $view, $path, array $shareKeys ) {
// $shareKeys must be an array with the following format:
// [userId] => [encrypted key]
$result = true;
foreach ( $shareKeys as $userId => $shareKey ) {
if ( !self::setShareKey( $view, $path, $userId, $shareKey ) ) {
// If any of the keys are not set, flag false
$result = false;
}
}
// Returns false if any of the keys weren't set
return $result;
}
/**
* @brief retrieve shareKey for an encrypted file
* @param \OC_FilesystemView $view
* @param string $userId
* @param string $filePath
* @internal param \OCA\Encryption\file $string name
* @return string file key or false
* @note The sharekey returned is encrypted. Decryption
* of the keyfile must be performed by client code
*/
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) {
// try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) {
$result = self::getShareKey( $view, $userId, self::fixPartialFilePath( $filePath ) );
if ( $result ) {
return $result;
}
}
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $filePath );
$shareKeyPath = \OC\Files\Filesystem::normalizePath( '/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey' );
if ( $view->file_exists( $shareKeyPath ) ) {
$result = $view->file_get_contents( $shareKeyPath );
} else {
$result = false;
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @brief delete all share keys of a given file
* @param \OC_FilesystemView $view
* @param string $userId owner of the file
* @param string $filePath path to the file, relative to the owners file dir
*/
public static function delAllShareKeys( \OC_FilesystemView $view, $userId, $filePath ) {
if ( $view->is_dir( $userId . '/files/' . $filePath ) ) {
$view->unlink( $userId . '/files_encryption/share-keys/' . $filePath );
} else {
$localKeyPath = $view->getLocalFile( $userId . '/files_encryption/share-keys/' . $filePath );
$matches = glob( preg_quote( $localKeyPath ) . '*.shareKey' );
foreach ( $matches as $ma ) {
$result = unlink( $ma );
if ( !$result ) {
\OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OC_Log::ERROR );
}
}
}
}
/**
* @brief Delete a single user's shareKey for a single file
*/
public static function delShareKey( \OC_FilesystemView $view, $userIds, $filePath ) {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() );
list( $owner, $filename ) = $util->getUidAndFilename( $filePath );
$shareKeyPath = \OC\Files\Filesystem::normalizePath( '/' . $owner . '/files_encryption/share-keys/' . $filename );
if ( $view->is_dir( $shareKeyPath ) ) {
$localPath = \OC\Files\Filesystem::normalizePath( $view->getLocalFolder( $shareKeyPath ) );
self::recursiveDelShareKeys( $localPath, $userIds );
} else {
foreach ( $userIds as $userId ) {
if ( !$view->unlink( $shareKeyPath . '.' . $userId . '.shareKey' ) ) {
\OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId . '.shareKey"', \OC_Log::ERROR );
}
}
}
\OC_FileProxy::$enabled = $proxyStatus;
}
/**
* @brief recursively delete share keys from given users
*
* @param string $dir directory
* @param array $userIds user ids for which the share keys should be deleted
*/
private static function recursiveDelShareKeys( $dir, $userIds ) {
foreach ( $userIds as $userId ) {
$matches = glob( preg_quote( $dir ) . '/*' . preg_quote( '.' . $userId . '.shareKey' ) );
}
/** @var $matches array */
foreach ( $matches as $ma ) {
if ( !unlink( $ma ) ) {
\OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $ma . '"', \OC_Log::ERROR );
}
}
$subdirs = $directories = glob( preg_quote( $dir ) . '/*', GLOB_ONLYDIR );
foreach ( $subdirs as $subdir ) {
self::recursiveDelShareKeys( $subdir, $userIds );
}
}
/**
* @brief Make preparations to vars and filesystem for saving a keyfile
*/
public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) {
$targetPath = ltrim( $path, '/' );
$path_parts = pathinfo( $targetPath );
// If the file resides within a subdirectory, create it
if (
isset( $path_parts['dirname'] )
&& ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] )
) {
$view->mkdir( $basePath . '/' . $path_parts['dirname'] );
}
return $targetPath;
}
/**
* @brief Fetch the legacy encryption key from user files
* @param string $login used to locate the legacy key
* @param string $passphrase used to decrypt the legacy key
* @return true / false
*
* if the key is left out, the default handler will be used
*/
public function getLegacyKey() {
$user = \OCP\User::getUser();
$view = new \OC_FilesystemView( '/' . $user );
return $view->file_get_contents( 'encryption.key' );
$targetPath = ltrim( $path, '/' );
$path_parts = pathinfo( $targetPath );
// If the file resides within a subdirectory, create it
if (
isset( $path_parts['dirname'] )
&& !$view->file_exists( $basePath . '/' . $path_parts['dirname'] )
) {
$sub_dirs = explode( DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname'] );
$dir = '';
foreach ( $sub_dirs as $sub_dir ) {
$dir .= '/' . $sub_dir;
if ( !$view->is_dir( $dir ) ) {
$view->mkdir( $dir );
}
}
}
return $targetPath;
}
}

View File

@ -1,41 +1,46 @@
<?php
/**
* ownCloud
*
* @author Sam Tuke, Robin Appelman
* @copyright 2012 Sam Tuke samtuke@owncloud.com, 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/>.
*
*/
* ownCloud
*
* @author Sam Tuke, Robin Appelman
* @copyright 2012 Sam Tuke samtuke@owncloud.com, 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/>.
*
*/
/**
* @brief Encryption proxy which handles filesystem operations before and after
* execution and encrypts, and handles keyfiles accordingly. Used for
* webui.
*/
* @brief Encryption proxy which handles filesystem operations before and after
* execution and encrypts, and handles keyfiles accordingly. Used for
* webui.
*/
namespace OCA\Encryption;
class Proxy extends \OC_FileProxy {
/**
* Class Proxy
* @package OCA\Encryption
*/
class Proxy extends \OC_FileProxy
{
private static $blackList = null; //mimetypes blacklisted from encryption
private static $enableEncryption = null;
/**
* Check if a file requires encryption
* @param string $path
@ -44,346 +49,417 @@ class Proxy extends \OC_FileProxy {
* Tests if server side encryption is enabled, and file is allowed by blacklists
*/
private static function shouldEncrypt( $path ) {
if ( is_null( self::$enableEncryption ) ) {
if (
\OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true'
&& Crypt::mode() == 'server'
if (
\OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true'
&& Crypt::mode() == 'server'
) {
self::$enableEncryption = true;
} else {
self::$enableEncryption = false;
}
}
if ( !self::$enableEncryption ) {
return false;
}
if ( is_null(self::$blackList ) ) {
self::$blackList = explode(',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
if ( is_null( self::$blackList ) ) {
self::$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) );
}
if ( Crypt::isCatfile( $path ) ) {
if ( Crypt::isCatfileContent( $path ) ) {
return true;
}
$extension = substr( $path, strrpos( $path, '.' ) +1 );
$extension = substr( $path, strrpos( $path, '.' ) + 1 );
if ( array_search( $extension, self::$blackList ) === false ) {
return true;
}
return false;
}
/**
* @param $path
* @param $data
* @return bool
*/
public function preFile_put_contents( $path, &$data ) {
if ( self::shouldEncrypt( $path ) ) {
if ( !is_resource( $data ) ) { //stream put contents should have been converted to fopen
// Stream put contents should have been converted to fopen
if ( !is_resource( $data ) ) {
$userId = \OCP\USER::getUser();
$rootView = new \OC_FilesystemView( '/' );
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $userId );
$session = new Session( $view );
$privateKey = $session->getPrivateKey();
$filePath = $util->stripUserFilesPath( $path );
// Set the filesize for userland, before encrypting
$size = strlen( $data );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// TODO: Check if file is shared, if so, use multiKeyEncrypt
// Encrypt plain data and fetch key
$encrypted = Crypt::keyEncryptKeyfile( $data, Keymanager::getPublicKey( $rootView, $userId ) );
// Replace plain content with encrypted content by reference
$data = $encrypted['data'];
$filePath = explode( '/', $path );
$filePath = array_slice( $filePath, 3 );
$filePath = '/' . implode( '/', $filePath );
// TODO: make keyfile dir dynamic from app config
$view = new \OC_FilesystemView( '/' );
// Check if there is an existing key we can reuse
if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) {
// Fetch shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $filePath );
// Decrypt the keyfile
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
} else {
// Make a new key
$plainKey = Crypt::generateKey();
}
// Encrypt data
$encData = Crypt::symmetricEncryptFileContent( $data, $plainKey );
$sharingEnabled = \OCP\Share::isEnabled();
// if file exists try to get sharing users
if ( $view->file_exists( $path ) ) {
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId );
} else {
$uniqueUserIds[] = $userId;
}
// Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds );
// Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys );
// Save sharekeys to user folders
Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] );
// Set encrypted keyfile as common varname
$encKey = $multiEncrypted['data'];
// Save keyfile for newly encrypted file in parallel directory tree
Keymanager::setFileKey( $view, $filePath, $userId, $encrypted['key'] );
Keymanager::setFileKey( $view, $filePath, $userId, $encKey );
// Replace plain content with encrypted content by reference
$data = $encData;
// Update the file cache with file info
\OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' );
\OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted' => true, 'size' => strlen( $data ), 'unencrypted_size' => $size ), '' );
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = true;
\OC_FileProxy::$enabled = $proxyStatus;
}
}
return true;
}
/**
* @param string $path Path of file from which has been read
* @param string $data Data that has been read from file
*/
public function postFile_get_contents( $path, $data ) {
// TODO: Use dependency injection to add required args for view and user etc. to this method
$userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new Util( $view, $userId );
$relPath = $util->stripUserFilesPath( $path );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// If data is a catfile
if (
Crypt::mode() == 'server'
&& Crypt::isCatfile( $data )
) {
$split = explode( '/', $path );
$filePath = array_slice( $split, 3 );
$filePath = '/' . implode( '/', $filePath );
//$cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
$view = new \OC_FilesystemView( '' );
$userId = \OCP\USER::getUser();
// TODO: Check if file is shared, if so, use multiKeyDecrypt
$encryptedKeyfile = Keymanager::getFileKey( $view, $userId, $filePath );
$session = new Session();
$decrypted = Crypt::keyDecryptKeyfile( $data, $encryptedKeyfile, $session->getPrivateKey( $split[1] ) );
} elseif (
Crypt::mode() == 'server'
&& isset( $_SESSION['legacyenckey'] )
&& Crypt::isEncryptedMeta( $path )
// init session
$session = new Session( $view );
// If data is a catfile
if (
Crypt::mode() == 'server'
&& Crypt::isCatfileContent( $data )
) {
$decrypted = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] );
$privateKey = $session->getPrivateKey( $userId );
// Get the encrypted keyfile
$encKeyfile = Keymanager::getFileKey( $view, $userId, $relPath );
// Attempt to fetch the user's shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $relPath );
// Decrypt keyfile with shareKey
$plainKeyfile = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey );
$plainData = Crypt::symmetricDecryptFileContent( $data, $plainKeyfile );
} elseif (
Crypt::mode() == 'server'
&& isset( $_SESSION['legacyenckey'] )
&& Crypt::isEncryptedMeta( $path )
) {
$plainData = Crypt::legacyDecrypt( $data, $session->getLegacyKey() );
}
\OC_FileProxy::$enabled = true;
if ( ! isset( $decrypted ) ) {
$decrypted = $data;
\OC_FileProxy::$enabled = $proxyStatus;
if ( !isset( $plainData ) ) {
$plainData = $data;
}
return $decrypted;
return $plainData;
}
/**
* @brief When a file is deleted, remove its keyfile also
*/
public function preUnlink( $path ) {
// Disable encryption proxy to prevent recursive calls
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\USER::getUser();
// Format path to be relative to user files dir
$trimmed = ltrim( $path, '/' );
$split = explode( '/', $trimmed );
$sliced = array_slice( $split, 2 );
$relPath = implode( '/', $sliced );
if ( $view->is_dir( $path ) ) {
// Dirs must be handled separately as deleteFileKey
// doesn't handle them
$view->unlink( $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/'. $relPath );
} else {
// Delete keyfile so it isn't orphaned
$result = Keymanager::deleteFileKey( $view, $userId, $relPath );
\OC_FileProxy::$enabled = true;
return $result;
// let the trashbin handle this
if ( \OCP\App::isEnabled( 'files_trashbin' ) ) {
return true;
}
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\USER::getUser();
$util = new Util( $view, $userId );
// Format path to be relative to user files dir
$relPath = $util->stripUserFilesPath( $path );
list( $owner, $ownerPath ) = $util->getUidAndFilename( $relPath );
// Delete keyfile & shareKey so it isn't orphaned
if ( !Keymanager::deleteFileKey( $view, $owner, $ownerPath ) ) {
\OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OC_Log::ERROR );
}
Keymanager::delAllShareKeys( $view, $owner, $ownerPath );
\OC_FileProxy::$enabled = $proxyStatus;
// If we don't return true then file delete will fail; better
// to leave orphaned keyfiles than to disallow file deletion
return true;
}
/**
* @brief When a file is renamed, rename its keyfile also
* @return bool Result of rename()
* @note This is pre rather than post because using post didn't work
* @param $path
* @return bool
*/
public function preRename( $oldPath, $newPath ) {
// Disable encryption proxy to prevent recursive calls
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\USER::getUser();
// Format paths to be relative to user files dir
$oldTrimmed = ltrim( $oldPath, '/' );
$oldSplit = explode( '/', $oldTrimmed );
$oldSliced = array_slice( $oldSplit, 2 );
$oldRelPath = implode( '/', $oldSliced );
$oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath . '.key';
$newTrimmed = ltrim( $newPath, '/' );
$newSplit = explode( '/', $newTrimmed );
$newSliced = array_slice( $newSplit, 2 );
$newRelPath = implode( '/', $newSliced );
$newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath . '.key';
// Rename keyfile so it isn't orphaned
$result = $view->rename( $oldKeyfilePath, $newKeyfilePath );
\OC_FileProxy::$enabled = true;
return $result;
public function postTouch( $path ) {
$this->handleFile( $path );
return true;
}
public function postFopen( $path, &$result ){
/**
* @param $path
* @param $result
* @return resource
*/
public function postFopen( $path, &$result ) {
if ( !$result ) {
return $result;
}
// Reformat path for use with OC_FSV
$path_split = explode( '/', $path );
$path_f = implode( array_slice( $path_split, 3 ) );
$path_f = implode( '/', array_slice( $path_split, 3 ) );
// FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
if ( count($path_split) >= 2 && $path_split[2] == 'cache' ) {
return $result;
}
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$meta = stream_get_meta_data( $result );
$view = new \OC_FilesystemView( '' );
$util = new Util( $view, \OCP\USER::getUser());
$util = new Util( $view, \OCP\USER::getUser() );
// If file is already encrypted, decrypt using crypto protocol
if (
Crypt::mode() == 'server'
&& $util->isEncryptedPath( $path )
if (
Crypt::mode() == 'server'
&& $util->isEncryptedPath( $path )
) {
// Close the original encrypted file
fclose( $result );
// Open the file using the crypto stream wrapper
// protocol and let it do the decryption work instead
$result = fopen( 'crypt://' . $path_f, $meta['mode'] );
} elseif (
self::shouldEncrypt( $path )
and $meta ['mode'] != 'r'
and $meta['mode'] != 'rb'
} elseif (
self::shouldEncrypt( $path )
and $meta ['mode'] != 'r'
and $meta['mode'] != 'rb'
) {
// If the file is not yet encrypted, but should be
// encrypted when it's saved (it's not read only)
// NOTE: this is the case for new files saved via WebDAV
if (
$view->file_exists( $path )
and $view->filesize( $path ) > 0
) {
$x = $view->file_get_contents( $path );
$tmp = tmpfile();
// // Make a temporary copy of the original file
// \OCP\Files::streamCopy( $result, $tmp );
//
// // Close the original stream, we'll return another one
// fclose( $result );
//
// $view->file_put_contents( $path_f, $tmp );
//
// fclose( $tmp );
}
$result = fopen( 'crypt://'.$path_f, $meta['mode'] );
$result = fopen( 'crypt://' . $path_f, $meta['mode'] );
}
// Re-enable the proxy
\OC_FileProxy::$enabled = true;
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
public function postGetMimeType( $path, $mime ) {
if ( Crypt::isCatfile( $path ) ) {
$mime = \OCP\Files::getMimeType( 'crypt://' . $path, 'w' );
}
return $mime;
}
/**
* @param $path
* @param $data
* @return array
*/
public function postGetFileInfo( $path, $data ) {
public function postStat( $path, $data ) {
if ( Crypt::isCatfile( $path ) ) {
$cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
$data['size'] = $cached['size'];
// if path is a folder do nothing
if ( is_array( $data ) && array_key_exists( 'size', $data ) ) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get file size
$data['size'] = self::postFileSize( $path, $data['size'] );
// Re-enable the proxy
\OC_FileProxy::$enabled = $proxyStatus;
}
return $data;
}
/**
* @param $path
* @param $size
* @return bool
*/
public function postFileSize( $path, $size ) {
if ( Crypt::isCatfile( $path ) ) {
$cached = \OC\Files\Filesystem::getFileInfo( $path, '' );
return $cached['size'];
} else {
$view = new \OC_FilesystemView( '/' );
// if path is a folder do nothing
if ( $view->is_dir( $path ) ) {
return $size;
}
// Reformat path for use with OC_FSV
$path_split = explode( '/', $path );
$path_f = implode( '/', array_slice( $path_split, 3 ) );
// if path is empty we cannot resolve anything
if ( empty( $path_f ) ) {
return $size;
}
$fileInfo = false;
// get file info from database/cache if not .part file
if ( !Keymanager::isPartialFilePath( $path ) ) {
$fileInfo = $view->getFileInfo( $path );
}
// if file is encrypted return real file size
if ( is_array( $fileInfo ) && $fileInfo['encrypted'] === true ) {
$size = $fileInfo['unencrypted_size'];
} else {
// self healing if file was removed from file cache
if ( !is_array( $fileInfo ) ) {
$fileInfo = array();
}
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
$fixSize = $util->getFileSize( $path );
if ( $fixSize > 0 ) {
$size = $fixSize;
$fileInfo['encrypted'] = true;
$fileInfo['unencrypted_size'] = $size;
// put file info if not .part file
if ( !Keymanager::isPartialFilePath( $path_f ) ) {
$view->putFileInfo( $path, $fileInfo );
}
}
}
return $size;
}
/**
* @param $path
*/
public function handleFile( $path ) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$session = new Session( $view );
$userId = \OCP\User::getUser();
$util = new Util( $view, $userId );
// Reformat path for use with OC_FSV
$path_split = explode( '/', $path );
$path_f = implode( '/', array_slice( $path_split, 3 ) );
// only if file is on 'files' folder fix file size and sharing
if ( count($path_split) >= 2 && $path_split[2] == 'files' && $util->fixFileSize( $path ) ) {
// get sharing app state
$sharingEnabled = \OCP\Share::isEnabled();
// get users
$usersSharing = $util->getSharingUsersArray( $sharingEnabled, $path_f );
// update sharing-keys
$util->setSharedFileKeyfiles( $session, $usersSharing, $path_f );
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}

View File

@ -26,78 +26,146 @@ namespace OCA\Encryption;
* Class for handling encryption related session data
*/
class Session {
class Session
{
private $view;
/**
* @brief if session is started, check if ownCloud key pair is set up, if not create it
* @param \OC_FilesystemView $view
*
* @note The ownCloud key pair is used to allow public link sharing even if encryption is enabled
*/
public function __construct( $view ) {
$this->view = $view;
if ( !$this->view->is_dir( 'owncloud_private_key' ) ) {
$this->view->mkdir( 'owncloud_private_key' );
}
$publicShareKeyId = \OC_Appconfig::getValue( 'files_encryption', 'publicShareKeyId' );
if ( $publicShareKeyId === null ) {
$publicShareKeyId = 'pubShare_' . substr( md5( time() ), 0, 8 );
\OC_Appconfig::setValue( 'files_encryption', 'publicShareKeyId', $publicShareKeyId );
}
if (
!$this->view->file_exists( "/public-keys/" . $publicShareKeyId . ".public.key" )
|| !$this->view->file_exists( "/owncloud_private_key/" . $publicShareKeyId . ".private.key" )
) {
$keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Save public key
if ( !$view->is_dir( '/public-keys' ) ) {
$view->mkdir( '/public-keys' );
}
$this->view->file_put_contents( '/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey'] );
// Encrypt private key empty passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], '' );
// Save private key
$this->view->file_put_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key', $encryptedPrivateKey );
\OC_FileProxy::$enabled = $proxyStatus;
}
if ( \OCP\USER::getUser() === false ||
( isset( $_GET['service'] ) && $_GET['service'] == 'files' &&
isset( $_GET['t'] ) )
) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key' );
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' );
$this->setPrivateKey( $privateKey );
\OC_FileProxy::$enabled = $proxyStatus;
}
}
/**
* @brief Sets user private key to session
* @param string $privateKey
* @return bool
*
*/
public function setPrivateKey( $privateKey ) {
$_SESSION['privateKey'] = $privateKey;
return true;
}
/**
* @brief Gets user private key from session
* @returns string $privateKey The user's plaintext private key
*
*/
public function getPrivateKey() {
if (
if (
isset( $_SESSION['privateKey'] )
&& !empty( $_SESSION['privateKey'] )
) {
return $_SESSION['privateKey'];
} else {
return false;
}
}
/**
* @brief Sets user legacy key to session
* @param $legacyKey
* @return bool
*
*/
public function setLegacyKey( $legacyKey ) {
if ( $_SESSION['legacyKey'] = $legacyKey ) {
return true;
}
$_SESSION['legacyKey'] = $legacyKey;
return true;
}
/**
* @brief Gets user legacy key from session
* @returns string $legacyKey The user's plaintext legacy key
*
*/
public function getLegacyKey() {
if (
if (
isset( $_SESSION['legacyKey'] )
&& !empty( $_SESSION['legacyKey'] )
) {
return $_SESSION['legacyKey'];
} else {
return false;
}
}
}

View File

@ -3,7 +3,7 @@
* ownCloud
*
* @author Robin Appelman
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>, 2011 Robin Appelman
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>, 2011 Robin Appelman
* <icewind1991@gmail.com>
*
* This library is free software; you can redistribute it and/or
@ -32,27 +32,29 @@ namespace OCA\Encryption;
/**
* @brief Provides 'crypt://' stream wrapper protocol.
* @note We use a stream wrapper because it is the most secure way to handle
* @note We use a stream wrapper because it is the most secure way to handle
* decrypted content transfers. There is no safe way to decrypt the entire file
* somewhere on the server, so we have to encrypt and decrypt blocks on the fly.
* @note Paths used with this protocol MUST BE RELATIVE. Use URLs like:
* crypt://filename, or crypt://subdirectory/filename, NOT
* crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in
* [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and
* crypt://filename, or crypt://subdirectory/filename, NOT
* crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in
* [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and
* will not be accessible to other methods.
* @note Data read and written must always be 8192 bytes long, as this is the
* buffer size used internally by PHP. The encryption process makes the input
* data longer, and input is chunked into smaller pieces in order to result in
* @note Data read and written must always be 8192 bytes long, as this is the
* buffer size used internally by PHP. The encryption process makes the input
* data longer, and input is chunked into smaller pieces in order to result in
* a 8192 encrypted block size.
* @note When files are deleted via webdav, or when they are updated and the
* previous version deleted, this is handled by OC\Files\View, and thus the
* encryption proxies are used and keyfiles deleted.
*/
class Stream {
class Stream
{
private $plainKey;
private $encKeyfiles;
public static $sourceStreams = array();
// TODO: make all below properties private again once unit testing is
// configured correctly
public $rawPath; // The raw path received by stream_open
public $path_f; // The raw path formatted to include username and data dir
private $rawPath; // The raw path relative to the data dir
private $relPath; // rel path to users file dir
private $userId;
private $handle; // Resource returned by fopen
private $path;
@ -60,117 +62,99 @@ class Stream {
private $meta = array(); // Header / meta for source stream
private $count;
private $writeCache;
public $size;
private $size;
private $unencryptedSize;
private $publicKey;
private $keyfile;
private $encKeyfile;
private static $view; // a fsview object set to user dir
private $rootView; // a fsview object set to '/'
/**
* @param $path
* @param $mode
* @param $options
* @param $opened_path
* @return bool
*/
public function stream_open( $path, $mode, $options, &$opened_path ) {
// Get access to filesystem via filesystemview object
if ( !self::$view ) {
self::$view = new \OC_FilesystemView( $this->userId . '/' );
if ( !isset( $this->rootView ) ) {
$this->rootView = new \OC_FilesystemView( '/' );
}
// Set rootview object if necessary
if ( ! $this->rootView ) {
$this->rootView = new \OC_FilesystemView( $this->userId . '/' );
$util = new Util( $this->rootView, \OCP\USER::getUser() );
}
$this->userId = \OCP\User::getUser();
// Get the bare file path
$path = str_replace( 'crypt://', '', $path );
$this->rawPath = $path;
$this->path_f = $this->userId . '/files/' . $path;
if (
dirname( $path ) == 'streams'
and isset( self::$sourceStreams[basename( $path )] )
$this->userId = $util->getUserId();
// Strip identifier text from path, this gives us the path relative to data/<user>/files
$this->relPath = \OC\Files\Filesystem::normalizePath( str_replace( 'crypt://', '', $path ) );
// rawPath is relative to the data directory
$this->rawPath = $util->getUserFilesDir() . $this->relPath;
// Disable fileproxies so we can get the file size and open the source file without recursive encryption
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if (
$mode == 'w'
or $mode == 'w+'
or $mode == 'wb'
or $mode == 'wb+'
) {
// Is this just for unit testing purposes?
$this->handle = self::$sourceStreams[basename( $path )]['stream'];
$this->path = self::$sourceStreams[basename( $path )]['path'];
$this->size = self::$sourceStreams[basename( $path )]['size'];
// We're writing a new file so start write counter with 0 bytes
$this->size = 0;
$this->unencryptedSize = 0;
} else {
if (
$mode == 'w'
or $mode == 'w+'
or $mode == 'wb'
or $mode == 'wb+'
) {
$this->size = 0;
} else {
$this->size = self::$view->filesize( $this->path_f, $mode );
//$this->size = filesize( $path );
}
// Disable fileproxies so we can open the source file without recursive encryption
\OC_FileProxy::$enabled = false;
//$this->handle = fopen( $path, $mode );
$this->handle = self::$view->fopen( $this->path_f, $mode );
\OC_FileProxy::$enabled = true;
if ( !is_resource( $this->handle ) ) {
\OCP\Util::writeLog( 'files_encryption', 'failed to open '.$path, \OCP\Util::ERROR );
}
$this->size = $this->rootView->filesize( $this->rawPath, $mode );
}
if ( is_resource( $this->handle ) ) {
$this->handle = $this->rootView->fopen( $this->rawPath, $mode );
\OC_FileProxy::$enabled = $proxyStatus;
if ( !is_resource( $this->handle ) ) {
\OCP\Util::writeLog( 'files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR );
} else {
$this->meta = stream_get_meta_data( $this->handle );
}
return is_resource( $this->handle );
}
/**
* @param $offset
* @param int $whence
*/
public function stream_seek( $offset, $whence = SEEK_SET ) {
$this->flush();
fseek( $this->handle, $offset, $whence );
}
public function stream_tell() {
return ftell($this->handle);
}
/**
* @param $count
* @return bool|string
* @throws \Exception
*/
public function stream_read( $count ) {
$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 );
@ -179,107 +163,89 @@ class Stream {
}
// $pos = ftell( $this->handle );
//
// Get the data from the file handle
$data = fread( $this->handle, 8192 );
if ( strlen( $data ) ) {
$this->getKey();
$result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile );
} else {
$result = '';
$result = '';
if ( strlen( $data ) ) {
if ( !$this->getKey() ) {
// Error! We don't have a key to decrypt the file with
throw new \Exception( 'Encryption key not found for "' . $this->rawPath . '" during attempted read via stream' );
}
// Decrypt data
$result = Crypt::symmetricDecryptFileContent( $data, $this->plainKey );
}
// $length = $this->size - $pos;
//
// if ( $length < 8192 ) {
//
// $result = substr( $result, 0, $length );
//
// }
return $result;
}
/**
* @brief Encrypt and pad data ready for writing to disk
* @param string $plainData data to be encrypted
* @param string $key key to use for encryption
* @return encrypted data on success, false on failure
* @return string encrypted data on success, false on failure
*/
public function preWriteEncrypt( $plainData, $key ) {
// Encrypt data to 'catfile', which includes IV
if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) {
return $encrypted;
return $encrypted;
} else {
return false;
}
}
/**
* @brief Get the keyfile for the current file, generate one if necessary
* @param bool $generate if true, a new key will be generated if none can be found
* @brief Fetch the plain encryption key for the file and set it as plainKey property
* @internal param bool $generate if true, a new key will be generated if none can be found
* @return bool true on key found and set, false on key not found and new key generated and set
*/
public function getKey() {
// If a keyfile already exists for a file named identically to
// file to be written
if ( self::$view->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->rawPath . '.key' ) ) {
// TODO: add error handling for when file exists but no
// keyfile
// Fetch existing keyfile
$this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->rawPath );
$this->getUser();
$session = new Session();
$privateKey = $session->getPrivateKey( $this->userId );
$this->keyfile = Crypt::keyDecrypt( $this->encKeyfile, $privateKey );
// Check if key is already set
if ( isset( $this->plainKey ) && isset( $this->encKeyfile ) ) {
return true;
}
// Fetch and decrypt keyfile
// Fetch existing keyfile
$this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->relPath );
// If a keyfile already exists
if ( $this->encKeyfile ) {
$session = new Session( $this->rootView );
$privateKey = $session->getPrivateKey( $this->userId );
$shareKey = Keymanager::getShareKey( $this->rootView, $this->userId, $this->relPath );
$this->plainKey = Crypt::multiKeyDecrypt( $this->encKeyfile, $shareKey, $privateKey );
return true;
} else {
return false;
}
}
public function getuser() {
// Only get the user again if it isn't already set
if ( empty( $this->userId ) ) {
// TODO: Move this user call out of here - it belongs
// elsewhere
$this->userId = \OCP\User::getUser();
}
// TODO: Add a method for getting the user in case OCP\User::
// getUser() doesn't work (can that scenario ever occur?)
}
/**
* @brief Handle plain data from the stream, and write it in 8192 byte blocks
* @param string $data data to be written to disk
@ -290,98 +256,54 @@ class Stream {
* @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek
*/
public function stream_write( $data ) {
// Disable the file proxies so that encryption is not
// automatically attempted when the file is written to disk -
// we are handling that separately here and we don't want to
// get into an infinite loop
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get the length of the unencrypted data that we are handling
$length = strlen( $data );
// So far this round, no data has been written
$written = 0;
// Find out where we are up to in the writing of data to the
// Find out where we are up to in the writing of data to the
// file
$pointer = ftell( $this->handle );
// Make sure the userId is set
$this->getuser();
// TODO: Check if file is shared, if so, use multiKeyEncrypt and
// save shareKeys in necessary user directories
// Get / generate the keyfile for the file we're handling
// If we're writing a new file (not overwriting an existing
// one), save the newly generated keyfile
if ( ! $this->getKey() ) {
$this->keyfile = Crypt::generateKey();
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
$this->encKeyfile = Crypt::keyEncrypt( $this->keyfile, $this->publicKey );
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\User::getUser();
// Save the new encrypted file key
Keymanager::setFileKey( $view, $this->rawPath, $userId, $this->encKeyfile );
if ( !$this->getKey() ) {
$this->plainKey = Crypt::generateKey();
}
// If extra data is left over from the last round, make sure it
// is integrated into the next 6126 / 8192 block
if ( $this->writeCache ) {
// Concat writeCache to start of $data
$data = $this->writeCache . $data;
// Clear the write cache, ready for resuse - it has been
// Clear the write cache, ready for reuse - it has been
// flushed and its old contents processed
$this->writeCache = '';
}
//
// // Make sure we always start on a block start
if ( 0 != ( $pointer % 8192 ) ) {
// if the current position of
// file indicator is not aligned to a 8192 byte block, fix it
// so that it is
// fseek( $this->handle, - ( $pointer % 8192 ), SEEK_CUR );
//
// $pointer = ftell( $this->handle );
//
// $unencryptedNewBlock = fread( $this->handle, 8192 );
//
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
//
// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->keyfile );
//
// $x = substr( $block, 0, $currentPos % 8192 );
//
// $data = $x . $data;
//
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
//
}
// While there still remains some data to be processed & written
while ( strlen( $data ) > 0 ) {
// Remaining length for this iteration, not of the
// entire file (may be greater than 8192 bytes)
$remainingLength = strlen( $data );
// If data remaining to be written is less than the
// size of 1 6126 byte block
if ( $remainingLength < 6126 ) {
// $currentPos = ftell( $this->handle );
// // While there still remains somed data to be processed & written
while( strlen( $data ) > 0 ) {
//
// // Remaining length for this iteration, not of the
// // entire file (may be greater than 8192 bytes)
// $remainingLength = strlen( $data );
//
// // If data remaining to be written is less than the
// // size of 1 6126 byte block
if ( strlen( $data ) < 6126 ) {
// Set writeCache to contents of $data
// The writeCache will be carried over to the
// next write round, and added to the start of
@ -394,98 +316,164 @@ class Stream {
// Clear $data ready for next round
$data = '';
//
} else {
// Read the chunk from the start of $data
$chunk = substr( $data, 0, 6126 );
$encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile );
$encrypted = $this->preWriteEncrypt( $chunk, $this->plainKey );
// Write the data chunk to disk. This will be
// attended to the last data chunk if the file
// being handled totals more than 6126 bytes
fwrite( $this->handle, $encrypted );
$writtenLen = strlen( $encrypted );
//fseek( $this->handle, $writtenLen, SEEK_CUR );
// Remove the chunk we just processed from
// Remove the chunk we just processed from
// $data, leaving only unprocessed data in $data
// var, for handling on the next round
$data = substr( $data, 6126 );
}
}
$this->size = max( $this->size, $pointer + $length );
$this->unencryptedSize += $length;
\OC_FileProxy::$enabled = $proxyStatus;
return $length;
}
/**
* @param $option
* @param $arg1
* @param $arg2
*/
public function stream_set_option( $option, $arg1, $arg2 ) {
switch($option) {
$return = false;
switch ( $option ) {
case STREAM_OPTION_BLOCKING:
stream_set_blocking( $this->handle, $arg1 );
$return = stream_set_blocking( $this->handle, $arg1 );
break;
case STREAM_OPTION_READ_TIMEOUT:
stream_set_timeout( $this->handle, $arg1, $arg2 );
$return = stream_set_timeout( $this->handle, $arg1, $arg2 );
break;
case STREAM_OPTION_WRITE_BUFFER:
stream_set_write_buffer( $this->handle, $arg1, $arg2 );
$return = stream_set_write_buffer( $this->handle, $arg1 );
}
return $return;
}
/**
* @return array
*/
public function stream_stat() {
return fstat($this->handle);
}
public function stream_lock( $mode ) {
flock( $this->handle, $mode );
}
public function stream_flush() {
return fflush( $this->handle );
// Not a typo: http://php.net/manual/en/function.fflush.php
return fstat( $this->handle );
}
/**
* @param $mode
*/
public function stream_lock( $mode ) {
return flock( $this->handle, $mode );
}
/**
* @return bool
*/
public function stream_flush() {
return fflush( $this->handle );
// Not a typo: http://php.net/manual/en/function.fflush.php
}
/**
* @return bool
*/
public function stream_eof() {
return feof($this->handle);
return feof( $this->handle );
}
private function flush() {
if ( $this->writeCache ) {
// Set keyfile property for file in question
$this->getKey();
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile );
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->plainKey );
fwrite( $this->handle, $encrypted );
$this->writeCache = '';
}
}
/**
* @return bool
*/
public function stream_close() {
$this->flush();
if (
$this->meta['mode']!='r'
and $this->meta['mode']!='rb'
if (
$this->meta['mode'] != 'r'
and $this->meta['mode'] != 'rb'
and $this->size > 0
) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
\OC\Files\Filesystem::putFileInfo( $this->path, array( 'encrypted' => true, 'size' => $this->size ), '' );
// Fetch user's public key
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
// Check if OC sharing api is enabled
$sharingEnabled = \OCP\Share::isEnabled();
$util = new Util( $this->rootView, $this->userId );
// Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId );
// Fetch public keys for all sharing users
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
// Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
$view = new \OC_FilesystemView( '/' );
// Save the new encrypted file key
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] );
// Save the sharekeys
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
// get file info
$fileInfo = $view->getFileInfo( $this->rawPath );
if ( !is_array( $fileInfo ) ) {
$fileInfo = array();
}
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// set encryption data
$fileInfo['encrypted'] = true;
$fileInfo['size'] = $this->size;
$fileInfo['unencrypted_size'] = $this->unencryptedSize;
// set fileinfo
$view->putFileInfo( $this->rawPath, $fileInfo );
}
return fclose( $this->handle );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) 2011 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
\OC_Util::checkAdminUser();
$tmpl = new OCP\Template( 'files_encryption', 'settings-admin' );
// Check if an adminRecovery account is enabled for recovering files after lost pwd
$view = new OC_FilesystemView( '' );
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
$tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
\OCP\Util::addscript( 'files_encryption', 'settings-admin' );
\OCP\Util::addscript( 'core', 'multiselect' );
return $tmpl->fetchPage();

View File

@ -6,12 +6,23 @@
* See the COPYING-README file.
*/
// Add CSS stylesheet
\OC_Util::addStyle( 'files_encryption', 'settings-personal' );
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
$user = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' );
$util = new \OCA\Encryption\Util( $view, $user );
$tmpl->assign( 'blacklist', $blackList );
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
\OCP\Util::addscript( 'files_encryption', 'settings-personal' );
\OCP\Util::addScript( 'settings', 'personal' );
$tmpl->assign( 'recoveryEnabled', $recoveryAdminEnabled );
$tmpl->assign( 'recoveryEnabledForUser', $recoveryEnabledForUser );
return $tmpl->fetchPage();
return null;

View File

@ -1,21 +0,0 @@
<?php
/**
* Copyright (c) 2011 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
\OC_Util::checkAdminUser();
$tmpl = new OCP\Template( 'files_encryption', 'settings' );
$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
$tmpl->assign( 'blacklist', $blackList );
$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) );
\OCP\Util::addscript( 'files_encryption', 'settings' );
\OCP\Util::addscript( 'core', 'multiselect' );
return $tmpl->fetchPage();

View File

@ -0,0 +1,56 @@
<form id="encryption">
<fieldset class="personalblock">
<p>
<strong><?php p($l->t( 'Encryption' )); ?></strong>
<br />
</p>
<p>
<?php p($l->t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?>
<br />
<br />
<input type="password" name="recoveryPassword" id="recoveryPassword" />
<label for="recoveryPassword"><?php p($l->t( "Recovery account password" )); ?></label>
<br />
<input
type='radio'
name='adminEnableRecovery'
value='1'
<?php echo ( $_["recoveryEnabled"] == 1 ? 'checked="checked"' : 'disabled' ); ?> />
<?php p($l->t( "Enabled" )); ?>
<br />
<input
type='radio'
name='adminEnableRecovery'
value='0'
<?php echo ( $_["recoveryEnabled"] == 0 ? 'checked="checked"' : 'disabled' ); ?> />
<?php p($l->t( "Disabled" )); ?>
</p>
<br /><br />
<p>
<strong><?php p($l->t( "Change encryption passwords recovery key:" )); ?></strong>
<br /><br />
<input
type="password"
name="changeRecoveryPassword"
id="oldRecoveryPassword"
<?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> />
<label for="oldRecoveryPassword"><?php p($l->t( "Old Recovery account password" )); ?></label>
<br />
<input
type="password"
name="changeRecoveryPassword"
id="newRecoveryPassword"
<?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> />
<label for="newRecoveryPassword"><?php p($l->t( "New Recovery account password" )); ?></label>
<br />
<button
type="button"
name="submitChangeRecoveryKey"
disabled><?php p($l->t( "Change Password" )); ?>
</button>
<span class="msg"></span>
</p>
</fieldset>
</form>

View File

@ -1,22 +1,33 @@
<form id="encryption">
<fieldset class="personalblock">
<legend>
<?php p($l->t( 'Encryption' )); ?>
<?php p( $l->t( 'Encryption' ) ); ?>
</legend>
<p>
<?php p($l->t( 'File encryption is enabled.' )); ?>
</p>
<?php if ( ! empty( $_["blacklist"] ) ): ?>
<p>
<?php p($l->t( 'The following file types will not be encrypted:' )); ?>
</p>
<ul>
<?php foreach( $_["blacklist"] as $type ): ?>
<li>
<?php p($type); ?>
</li>
<?php endforeach; ?>
</ul>
<?php if ( $_["recoveryEnabled"] ): ?>
<p>
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery by sharing all files with your administrator:" ) ); ?></label>
<br />
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?></em>
<br />
<input
type='radio'
name='userEnableRecovery'
value='1'
<?php echo ( $_["recoveryEnabledForUser"] == 1 ? 'checked="checked"' : '' ); ?> />
<?php p( $l->t( "Enabled" ) ); ?>
<br />
<input
type='radio'
name='userEnableRecovery'
value='0'
<?php echo ( $_["recoveryEnabledForUser"] == 0 ? 'checked="checked"' : '' ); ?> />
<?php p( $l->t( "Disabled" ) ); ?>
<div id="recoveryEnabledSuccess"><?php p( $l->t( 'File recovery settings updated' ) ); ?></div>
<div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
</p>
<?php endif; ?>
<br />
</fieldset>
</form>

View File

@ -1,20 +0,0 @@
<form id="encryption">
<fieldset class="personalblock">
<p>
<strong><?php p($l->t( 'Encryption' )); ?></strong>
<?php p($l->t( "Exclude the following file types from encryption:" )); ?>
<br />
<select
id='encryption_blacklist'
title="<?php p($l->t( 'None' ))?>"
multiple="multiple">
<?php foreach($_["blacklist"] as $type): ?>
<option selected="selected" value="<?php p($type); ?>"> <?php p($type); ?> </option>
<?php endforeach;?>
</select>
</p>
</fieldset>
</form>

View File

@ -1,667 +0,0 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and
* Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
//require_once "PHPUnit/Framework/TestCase.php";
require_once realpath( dirname(__FILE__).'/../../../3rdparty/Crypt_Blowfish/Blowfish.php' );
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
use OCA\Encryption;
// This has to go here because otherwise session errors arise, and the private
// encryption key needs to be saved in the session
\OC_User::login( 'admin', 'admin' );
/**
* @note It would be better to use Mockery here for mocking out the session
* handling process, and isolate calls to session class and data from the unit
* tests relating to them (stream etc.). However getting mockery to work and
* overload classes whilst also using the OC autoloader is difficult due to
* load order Pear errors.
*/
class Test_Crypt extends \PHPUnit_Framework_TestCase {
function setUp() {
// set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
$this->dataShort = 'hats';
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
$this->randomKey = Encryption\Crypt::generateKey();
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView( '/' );
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
\OC_Filesystem::init( '/' );
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
}
function tearDown() {
}
function testGenerateKey() {
# TODO: use more accurate (larger) string length for test confirmation
$key = Encryption\Crypt::generateKey();
$this->assertTrue( strlen( $key ) > 16 );
}
function testGenerateIv() {
$iv = Encryption\Crypt::generateIv();
$this->assertEquals( 16, strlen( $iv ) );
return $iv;
}
/**
* @depends testGenerateIv
*/
function testConcatIv( $iv ) {
$catFile = Encryption\Crypt::concatIv( $this->dataLong, $iv );
// Fetch encryption metadata from end of file
$meta = substr( $catFile, -22 );
$identifier = substr( $meta, 0, 6);
// Fetch IV from end of file
$foundIv = substr( $meta, 6 );
$this->assertEquals( '00iv00', $identifier );
$this->assertEquals( $iv, $foundIv );
// Remove IV and IV identifier text to expose encrypted content
$data = substr( $catFile, 0, -22 );
$this->assertEquals( $this->dataLong, $data );
return array(
'iv' => $iv
, 'catfile' => $catFile
);
}
/**
* @depends testConcatIv
*/
function testSplitIv( $testConcatIv ) {
// Split catfile into components
$splitCatfile = Encryption\Crypt::splitIv( $testConcatIv['catfile'] );
// Check that original IV and split IV match
$this->assertEquals( $testConcatIv['iv'], $splitCatfile['iv'] );
// Check that original data and split data match
$this->assertEquals( $this->dataLong, $splitCatfile['encrypted'] );
}
function testAddPadding() {
$padded = Encryption\Crypt::addPadding( $this->dataLong );
$padding = substr( $padded, -2 );
$this->assertEquals( 'xx' , $padding );
return $padded;
}
/**
* @depends testAddPadding
*/
function testRemovePadding( $padded ) {
$noPadding = Encryption\Crypt::RemovePadding( $padded );
$this->assertEquals( $this->dataLong, $noPadding );
}
function testEncrypt() {
$random = openssl_random_pseudo_bytes( 13 );
$iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht
$crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' );
$this->assertNotEquals( $this->dataUrl, $crypted );
}
function testDecrypt() {
$random = openssl_random_pseudo_bytes( 13 );
$iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht
$crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' );
$decrypt = Encryption\Crypt::decrypt( $crypted, $iv, 'hat' );
$this->assertEquals( $this->dataUrl, $decrypt );
}
function testSymmetricEncryptFileContent() {
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$crypted = Encryption\Crypt::symmetricEncryptFileContent( $this->dataShort, 'hat' );
$this->assertNotEquals( $this->dataShort, $crypted );
$decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted, 'hat' );
$this->assertEquals( $this->dataShort, $decrypt );
}
// These aren't used for now
// function testSymmetricBlockEncryptShortFileContent() {
//
// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataShort, $this->randomKey );
//
// $this->assertNotEquals( $this->dataShort, $crypted );
//
//
// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey );
//
// $this->assertEquals( $this->dataShort, $decrypt );
//
// }
//
// function testSymmetricBlockEncryptLongFileContent() {
//
// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataLong, $this->randomKey );
//
// $this->assertNotEquals( $this->dataLong, $crypted );
//
//
// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey );
//
// $this->assertEquals( $this->dataLong, $decrypt );
//
// }
function testSymmetricStreamEncryptShortFileContent() {
$filename = 'tmp-'.time();
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
// Check that the file was encrypted before being written to disk
$this->assertNotEquals( $this->dataShort, $retreivedCryptedFile );
// Get private key
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
// Get keyfile
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Manually decrypt
$manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile );
// Check that decrypted data matches
$this->assertEquals( $this->dataShort, $manualDecrypt );
}
/**
* @brief Test that data that is written by the crypto stream wrapper
* @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read
* @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
* reassembly of its data
*/
function testSymmetricStreamEncryptLongFileContent() {
// Generate a a random filename
$filename = 'tmp-'.time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n";
// Check that the file was encrypted before being written to disk
$this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile );
// Manuallly split saved file into separate IVs and encrypted chunks
$r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE);
//print_r($r);
// Join IVs and their respective data chunks
$e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] );
//print_r($e);
// Get private key
$encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
$decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass );
// Get keyfile
$encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename );
$decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey );
// Set var for reassembling decrypted content
$decrypt = '';
// Manually decrypt chunk
foreach ($e as $e) {
// echo "\n\$e = $e";
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile );
// Assemble decrypted chunks
$decrypt .= $chunkDecrypt;
// echo "\n\$chunkDecrypt = $chunkDecrypt";
}
// echo "\n\$decrypt = $decrypt";
$this->assertEquals( $this->dataLong.$this->dataLong, $decrypt );
// Teardown
$this->view->unlink( $filename );
Encryption\Keymanager::deleteFileKey( $filename );
}
/**
* @brief Test that data that is read by the crypto stream wrapper
*/
function testSymmetricStreamDecryptShortFileContent() {
$filename = 'tmp-'.time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataShort, $decrypt );
}
function testSymmetricStreamDecryptLongFileContent() {
$filename = 'tmp-'.time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong );
// Test that data was successfully written
$this->assertTrue( is_int( $cryptedFile ) );
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename );
$decrypt = file_get_contents( 'crypt://' . $filename );
$this->assertEquals( $this->dataLong, $decrypt );
}
// Is this test still necessary?
// function testSymmetricBlockStreamDecryptFileContent() {
//
// \OC_User::setUserId( 'admin' );
//
// // Disable encryption proxy to prevent unwanted en/decryption
// \OC_FileProxy::$enabled = false;
//
// $cryptedFile = file_put_contents( 'crypt://' . '/blockEncrypt', $this->dataUrl );
//
// // Disable encryption proxy to prevent unwanted en/decryption
// \OC_FileProxy::$enabled = false;
//
// echo "\n\n\$cryptedFile = " . $this->view->file_get_contents( '/blockEncrypt' );
//
// $retreivedCryptedFile = file_get_contents( 'crypt://' . '/blockEncrypt' );
//
// $this->assertEquals( $this->dataUrl, $retreivedCryptedFile );
//
// \OC_FileProxy::$enabled = false;
//
// }
function testSymmetricEncryptFileContentKeyfile() {
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl );
$this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] );
$decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted['encrypted'], $crypted['key'] );
$this->assertEquals( $this->dataUrl, $decrypt );
}
function testIsEncryptedContent() {
$this->assertFalse( Encryption\Crypt::isCatfile( $this->dataUrl ) );
$this->assertFalse( Encryption\Crypt::isCatfile( $this->legacyEncryptedData ) );
$keyfileContent = Encryption\Crypt::symmetricEncryptFileContent( $this->dataUrl, 'hat' );
$this->assertTrue( Encryption\Crypt::isCatfile( $keyfileContent ) );
}
function testMultiKeyEncrypt() {
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$pair1 = Encryption\Crypt::createKeypair();
$this->assertEquals( 2, count( $pair1 ) );
$this->assertTrue( strlen( $pair1['publicKey'] ) > 1 );
$this->assertTrue( strlen( $pair1['privateKey'] ) > 1 );
$crypted = Encryption\Crypt::multiKeyEncrypt( $this->dataUrl, array( $pair1['publicKey'] ) );
$this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] );
$decrypt = Encryption\Crypt::multiKeyDecrypt( $crypted['encrypted'], $crypted['keys'][0], $pair1['privateKey'] );
$this->assertEquals( $this->dataUrl, $decrypt );
}
function testKeyEncrypt() {
// Generate keypair
$pair1 = Encryption\Crypt::createKeypair();
// Encrypt data
$crypted = Encryption\Crypt::keyEncrypt( $this->dataUrl, $pair1['publicKey'] );
$this->assertNotEquals( $this->dataUrl, $crypted );
// Decrypt data
$decrypt = Encryption\Crypt::keyDecrypt( $crypted, $pair1['privateKey'] );
$this->assertEquals( $this->dataUrl, $decrypt );
}
// What is the point of this test? It doesn't use keyEncryptKeyfile()
function testKeyEncryptKeyfile() {
# TODO: Don't repeat encryption from previous tests, use PHPUnit test interdependency instead
// Generate keypair
$pair1 = Encryption\Crypt::createKeypair();
// Encrypt plain data, generate keyfile & encrypted file
$cryptedData = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl );
// Encrypt keyfile
$cryptedKey = Encryption\Crypt::keyEncrypt( $cryptedData['key'], $pair1['publicKey'] );
// Decrypt keyfile
$decryptKey = Encryption\Crypt::keyDecrypt( $cryptedKey, $pair1['privateKey'] );
// Decrypt encrypted file
$decryptData = Encryption\Crypt::symmetricDecryptFileContent( $cryptedData['encrypted'], $decryptKey );
$this->assertEquals( $this->dataUrl, $decryptData );
}
/**
* @brief test functionality of keyEncryptKeyfile() and
* keyDecryptKeyfile()
*/
function testKeyDecryptKeyfile() {
$encrypted = Encryption\Crypt::keyEncryptKeyfile( $this->dataShort, $this->genPublicKey );
$this->assertNotEquals( $encrypted['data'], $this->dataShort );
$decrypted = Encryption\Crypt::keyDecryptKeyfile( $encrypted['data'], $encrypted['key'], $this->genPrivateKey );
$this->assertEquals( $decrypted, $this->dataShort );
}
/**
* @brief test encryption using legacy blowfish method
*/
function testLegacyEncryptShort() {
$crypted = Encryption\Crypt::legacyEncrypt( $this->dataShort, $this->pass );
$this->assertNotEquals( $this->dataShort, $crypted );
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
return $crypted;
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptShort
*/
function testLegacyDecryptShort( $crypted ) {
$decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass );
$this->assertEquals( $this->dataShort, $decrypted );
}
/**
* @brief test encryption using legacy blowfish method
*/
function testLegacyEncryptLong() {
$crypted = Encryption\Crypt::legacyEncrypt( $this->dataLong, $this->pass );
$this->assertNotEquals( $this->dataLong, $crypted );
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
return $crypted;
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptLong
*/
function testLegacyDecryptLong( $crypted ) {
$decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass );
$this->assertEquals( $this->dataLong, $decrypted );
}
/**
* @brief test generation of legacy encryption key
* @depends testLegacyDecryptShort
*/
function testLegacyCreateKey() {
// Create encrypted key
$encKey = Encryption\Crypt::legacyCreateKey( $this->pass );
// Decrypt key
$key = Encryption\Crypt::legacyDecrypt( $encKey, $this->pass );
$this->assertTrue( is_numeric( $key ) );
// Check that key is correct length
$this->assertEquals( 20, strlen( $key ) );
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptLong
*/
function testLegacyKeyRecryptKeyfileEncrypt( $crypted ) {
$recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile( $crypted, $this->pass, $this->genPublicKey, $this->pass );
$this->assertNotEquals( $this->dataLong, $recrypted['data'] );
return $recrypted;
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
}
// function testEncryption(){
//
// $key=uniqid();
// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
// $source=file_get_contents($file); //nice large text file
// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
// $decrypted=rtrim($decrypted, "\0");
// $this->assertNotEquals($encrypted,$source);
// $this->assertEquals($decrypted,$source);
//
// $chunk=substr($source,0,8192);
// $encrypted=OC_Encryption\Crypt::encrypt($chunk,$key);
// $this->assertEquals(strlen($chunk),strlen($encrypted));
// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
// $decrypted=rtrim($decrypted, "\0");
// $this->assertEquals($decrypted,$chunk);
//
// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
// $this->assertNotEquals($encrypted,$source);
// $this->assertEquals($decrypted,$source);
//
// $tmpFileEncrypted=OCP\Files::tmpFile();
// OC_Encryption\Crypt::encryptfile($file,$tmpFileEncrypted,$key);
// $encrypted=file_get_contents($tmpFileEncrypted);
// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
// $this->assertNotEquals($encrypted,$source);
// $this->assertEquals($decrypted,$source);
//
// $tmpFileDecrypted=OCP\Files::tmpFile();
// OC_Encryption\Crypt::decryptfile($tmpFileEncrypted,$tmpFileDecrypted,$key);
// $decrypted=file_get_contents($tmpFileDecrypted);
// $this->assertEquals($decrypted,$source);
//
// $file=OC::$SERVERROOT.'/core/img/weather-clear.png';
// $source=file_get_contents($file); //binary file
// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
// $decrypted=rtrim($decrypted, "\0");
// $this->assertEquals($decrypted,$source);
//
// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key);
// $this->assertEquals($decrypted,$source);
//
// }
//
// function testBinary(){
// $key=uniqid();
//
// $file=__DIR__.'/binary';
// $source=file_get_contents($file); //binary file
// $encrypted=OC_Encryption\Crypt::encrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key);
//
// $decrypted=rtrim($decrypted, "\0");
// $this->assertEquals($decrypted,$source);
//
// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key);
// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key,strlen($source));
// $this->assertEquals($decrypted,$source);
// }
}

View File

@ -1,130 +0,0 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
//require_once "PHPUnit/Framework/TestCase.php";
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
use OCA\Encryption;
// This has to go here because otherwise session errors arise, and the private
// encryption key needs to be saved in the session
\OC_User::login( 'admin', 'admin' );
class Test_Keymanager extends \PHPUnit_Framework_TestCase {
function setUp() {
\OC_FileProxy::$enabled = false;
// set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
$this->dataShort = 'hats';
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
$this->randomKey = Encryption\Crypt::generateKey();
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView( '/' );
\OC_User::setUserId( 'admin' );
$this->userId = 'admin';
$this->pass = 'admin';
\OC_Filesystem::init( '/' );
\OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' );
}
function tearDown(){
\OC_FileProxy::$enabled = true;
}
function testGetPrivateKey() {
$key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
// Will this length vary? Perhaps we should use a range instead
$this->assertEquals( 2296, strlen( $key ) );
}
function testGetPublicKey() {
$key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId );
$this->assertEquals( 451, strlen( $key ) );
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) );
}
function testSetFileKey() {
# NOTE: This cannot be tested until we are able to break out
# of the FileSystemView data directory root
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' );
$path = 'unittest-'.time().'txt';
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] );
}
// /**
// * @depends testGetPrivateKey
// */
// function testGetPrivateKey_decrypt() {
//
// $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId );
//
// # TODO: replace call to Crypt with a mock object?
// $decrypted = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->passphrase );
//
// $this->assertEquals( 1704, strlen( $decrypted ) );
//
// $this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $decrypted, 0, 27 ) );
//
// }
function testGetUserKeys() {
$keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId );
$this->assertEquals( 451, strlen( $keys['publicKey'] ) );
$this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) );
$this->assertEquals( 2296, strlen( $keys['privateKey'] ) );
}
function testGetPublicKeys() {
# TODO: write me
}
function testGetFileKey() {
// Encryption\Keymanager::getFileKey( $this->view, $this->userId, $this->filePath );
}
}

View File

@ -1,226 +0,0 @@
// <?php
// /**
// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
// * This file is licensed under the Affero General Public License version 3 or
// * later.
// * See the COPYING-README file.
// */
//
// namespace OCA\Encryption;
//
// class Test_Stream extends \PHPUnit_Framework_TestCase {
//
// function setUp() {
//
// \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
//
// $this->empty = '';
//
// $this->stream = new Stream();
//
// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
// $this->dataShort = 'hats';
//
// $this->emptyTmpFilePath = \OCP\Files::tmpFile();
//
// $this->dataTmpFilePath = \OCP\Files::tmpFile();
//
// file_put_contents( $this->dataTmpFilePath, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est." );
//
// }
//
// function testStreamOpen() {
//
// $stream1 = new Stream();
//
// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'wb', array(), $this->empty );
//
// // Test that resource was returned successfully
// $this->assertTrue( $handle1 );
//
// // Test that file has correct size
// $this->assertEquals( 0, $stream1->size );
//
// // Test that path is correct
// $this->assertEquals( $this->emptyTmpFilePath, $stream1->rawPath );
//
// $stream2 = new Stream();
//
// $handle2 = $stream2->stream_open( 'crypt://' . $this->emptyTmpFilePath, 'wb', array(), $this->empty );
//
// // Test that protocol identifier is removed from path
// $this->assertEquals( $this->emptyTmpFilePath, $stream2->rawPath );
//
// // "Stat failed error" prevents this test from executing
// // $stream3 = new Stream();
// //
// // $handle3 = $stream3->stream_open( $this->dataTmpFilePath, 'r', array(), $this->empty );
// //
// // $this->assertEquals( 0, $stream3->size );
//
// }
//
// function testStreamWrite() {
//
// $stream1 = new Stream();
//
// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'r+b', array(), $this->empty );
//
// # what about the keymanager? there is no key for the newly created temporary file!
//
// $stream1->stream_write( $this->dataShort );
//
// }
//
// // function getStream( $id, $mode, $size ) {
// //
// // if ( $id === '' ) {
// //
// // $id = uniqid();
// // }
// //
// //
// // if ( !isset( $this->tmpFiles[$id] ) ) {
// //
// // // If tempfile with given name does not already exist, create it
// //
// // $file = OCP\Files::tmpFile();
// //
// // $this->tmpFiles[$id] = $file;
// //
// // } else {
// //
// // $file = $this->tmpFiles[$id];
// //
// // }
// //
// // $stream = fopen( $file, $mode );
// //
// // Stream::$sourceStreams[$id] = array( 'path' => 'dummy' . $id, 'stream' => $stream, 'size' => $size );
// //
// // return fopen( 'crypt://streams/'.$id, $mode );
// //
// // }
// //
// // function testStream( ){
// //
// // $stream = $this->getStream( 'test1', 'w', strlen( 'foobar' ) );
// //
// // fwrite( $stream, 'foobar' );
// //
// // fclose( $stream );
// //
// //
// // $stream = $this->getStream( 'test1', 'r', strlen( 'foobar' ) );
// //
// // $data = fread( $stream, 6 );
// //
// // fclose( $stream );
// //
// // $this->assertEquals( 'foobar', $data );
// //
// //
// // $file = OC::$SERVERROOT.'/3rdparty/MDB2.php';
// //
// // $source = fopen( $file, 'r' );
// //
// // $target = $this->getStream( 'test2', 'w', 0 );
// //
// // OCP\Files::streamCopy( $source, $target );
// //
// // fclose( $target );
// //
// // fclose( $source );
// //
// //
// // $stream = $this->getStream( 'test2', 'r', filesize( $file ) );
// //
// // $data = stream_get_contents( $stream );
// //
// // $original = file_get_contents( $file );
// //
// // $this->assertEquals( strlen( $original ), strlen( $data ) );
// //
// // $this->assertEquals( $original, $data );
// //
// // }
//
// }
//
// // class Test_CryptStream extends PHPUnit_Framework_TestCase {
// // private $tmpFiles=array();
// //
// // function testStream(){
// // $stream=$this->getStream('test1','w',strlen('foobar'));
// // fwrite($stream,'foobar');
// // fclose($stream);
// //
// // $stream=$this->getStream('test1','r',strlen('foobar'));
// // $data=fread($stream,6);
// // fclose($stream);
// // $this->assertEquals('foobar',$data);
// //
// // $file=OC::$SERVERROOT.'/3rdparty/MDB2.php';
// // $source=fopen($file,'r');
// // $target=$this->getStream('test2','w',0);
// // OCP\Files::streamCopy($source,$target);
// // fclose($target);
// // fclose($source);
// //
// // $stream=$this->getStream('test2','r',filesize($file));
// // $data=stream_get_contents($stream);
// // $original=file_get_contents($file);
// // $this->assertEquals(strlen($original),strlen($data));
// // $this->assertEquals($original,$data);
// // }
// //
// // /**
// // * get a cryptstream to a temporary file
// // * @param string $id
// // * @param string $mode
// // * @param int size
// // * @return resource
// // */
// // function getStream($id,$mode,$size){
// // if($id===''){
// // $id=uniqid();
// // }
// // if(!isset($this->tmpFiles[$id])){
// // $file=OCP\Files::tmpFile();
// // $this->tmpFiles[$id]=$file;
// // }else{
// // $file=$this->tmpFiles[$id];
// // }
// // $stream=fopen($file,$mode);
// // OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id,'stream'=>$stream,'size'=>$size);
// // return fopen('crypt://streams/'.$id,$mode);
// // }
// //
// // function testBinary(){
// // $file=__DIR__.'/binary';
// // $source=file_get_contents($file);
// //
// // $stream=$this->getStream('test','w',strlen($source));
// // fwrite($stream,$source);
// // fclose($stream);
// //
// // $stream=$this->getStream('test','r',strlen($source));
// // $data=stream_get_contents($stream);
// // fclose($stream);
// // $this->assertEquals(strlen($data),strlen($source));
// // $this->assertEquals($source,$data);
// //
// // $file=__DIR__.'/zeros';
// // $source=file_get_contents($file);
// //
// // $stream=$this->getStream('test2','w',strlen($source));
// // fwrite($stream,$source);
// // fclose($stream);
// //
// // $stream=$this->getStream('test2','r',strlen($source));
// // $data=stream_get_contents($stream);
// // fclose($stream);
// // $this->assertEquals(strlen($data),strlen($source));
// // $this->assertEquals($source,$data);
// // }
// // }

View File

@ -1,225 +0,0 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
//require_once "PHPUnit/Framework/TestCase.php";
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
require_once realpath( dirname(__FILE__).'/../lib/crypt.php' );
require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' );
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' );
require_once realpath( dirname(__FILE__).'/../lib/stream.php' );
require_once realpath( dirname(__FILE__).'/../lib/util.php' );
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' );
// Load mockery files
require_once 'Mockery/Loader.php';
require_once 'Hamcrest/Hamcrest.php';
$loader = new \Mockery\Loader;
$loader->register();
use \Mockery as m;
use OCA\Encryption;
class Test_Enc_Util extends \PHPUnit_Framework_TestCase {
function setUp() {
\OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' );
// set content for encrypting / decrypting in tests
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' );
$this->dataShort = 'hats';
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
$this->userId = 'admin';
$this->pass = 'admin';
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
$this->view = new \OC_FilesystemView( '/' );
$this->mockView = m::mock('OC_FilesystemView');
$this->util = new Encryption\Util( $this->mockView, $this->userId );
}
function tearDown(){
m::close();
}
/**
* @brief test that paths set during User construction are correct
*/
function testKeyPaths() {
$mockView = m::mock('OC_FilesystemView');
$util = new Encryption\Util( $mockView, $this->userId );
$this->assertEquals( $this->publicKeyDir, $util->getPath( 'publicKeyDir' ) );
$this->assertEquals( $this->encryptionDir, $util->getPath( 'encryptionDir' ) );
$this->assertEquals( $this->keyfilesPath, $util->getPath( 'keyfilesPath' ) );
$this->assertEquals( $this->publicKeyPath, $util->getPath( 'publicKeyPath' ) );
$this->assertEquals( $this->privateKeyPath, $util->getPath( 'privateKeyPath' ) );
}
/**
* @brief test setup of encryption directories when they don't yet exist
*/
function testSetupServerSideNotSetup() {
$mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false );
$mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId );
$this->assertEquals( true, $util->setupServerSide( $this->pass ) );
}
/**
* @brief test setup of encryption directories when they already exist
*/
function testSetupServerSideIsSetup() {
$mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId );
$this->assertEquals( true, $util->setupServerSide( $this->pass ) );
}
/**
* @brief test checking whether account is ready for encryption, when it isn't ready
*/
function testReadyNotReady() {
$mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(1)->andReturn( false );
$util = new Encryption\Util( $mockView, $this->userId );
$this->assertEquals( false, $util->ready() );
# TODO: Add more tests here to check that if any of the dirs are
# then false will be returned. Use strict ordering?
}
/**
* @brief test checking whether account is ready for encryption, when it is ready
*/
function testReadyIsReady() {
$mockView = m::mock('OC_FilesystemView');
$mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true );
$util = new Encryption\Util( $mockView, $this->userId );
$this->assertEquals( true, $util->ready() );
# TODO: Add more tests here to check that if any of the dirs are
# then false will be returned. Use strict ordering?
}
function testFindFiles() {
// $this->view->chroot( "/data/{$this->userId}/files" );
$util = new Encryption\Util( $this->view, $this->userId );
$files = $util->findFiles( '/', 'encrypted' );
var_dump( $files );
# TODO: Add more tests here to check that if any of the dirs are
# then false will be returned. Use strict ordering?
}
// /**
// * @brief test decryption using legacy blowfish method
// * @depends testLegacyEncryptLong
// */
// function testLegacyKeyRecryptKeyfileDecrypt( $recrypted ) {
//
// $decrypted = Encryption\Crypt::keyDecryptKeyfile( $recrypted['data'], $recrypted['key'], $this->genPrivateKey );
//
// $this->assertEquals( $this->dataLong, $decrypted );
//
// }
// // Cannot use this test for now due to hidden dependencies in OC_FileCache
// function testIsLegacyEncryptedContent() {
//
// $keyfileContent = OCA\Encryption\Crypt::symmetricEncryptFileContent( $this->legacyEncryptedData, 'hat' );
//
// $this->assertFalse( OCA\Encryption\Crypt::isLegacyEncryptedContent( $keyfileContent, '/files/admin/test.txt' ) );
//
// OC_FileCache::put( '/admin/files/legacy-encrypted-test.txt', $this->legacyEncryptedData );
//
// $this->assertTrue( OCA\Encryption\Crypt::isLegacyEncryptedContent( $this->legacyEncryptedData, '/files/admin/test.txt' ) );
//
// }
// // Cannot use this test for now due to need for different root in OC_Filesystem_view class
// function testGetLegacyKey() {
//
// $c = new \OCA\Encryption\Util( $view, false );
//
// $bool = $c->getLegacyKey( 'admin' );
//
// $this->assertTrue( $bool );
//
// $this->assertTrue( $c->legacyKey );
//
// $this->assertTrue( is_int( $c->legacyKey ) );
//
// $this->assertTrue( strlen( $c->legacyKey ) == 20 );
//
// }
// // Cannot use this test for now due to need for different root in OC_Filesystem_view class
// function testLegacyDecrypt() {
//
// $c = new OCA\Encryption\Util( $this->view, false );
//
// $bool = $c->getLegacyKey( 'admin' );
//
// $encrypted = $c->legacyEncrypt( $this->data, $c->legacyKey );
//
// $decrypted = $c->legacyDecrypt( $encrypted, $c->legacyKey );
//
// $this->assertEquals( $decrypted, $this->data );
//
// }
}

View File

@ -0,0 +1,833 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and
* Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php');
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../lib/helper.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Crypt
*/
class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase
{
public $userId;
public $pass;
public $stateFilesTrashbin;
public $dataLong;
public $dataUrl;
public $dataShort;
/**
* @var OC_FilesystemView
*/
public $view;
public $legacyEncryptedData;
public $genPrivateKey;
public $genPublicKey;
function setUp()
{
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
// set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php'));
$this->dataShort = 'hats';
$this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php');
$this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt');
$this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt');
$this->legacyEncryptedDataKey = realpath(dirname(__FILE__) . '/encryption.key');
$this->randomKey = Encryption\Crypt::generateKey();
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView('/');
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
$userHome = \OC_User::getHome($this->userId);
$this->dataDir = str_replace('/' . $this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
// Filesystem related hooks
\OCA\Encryption\Helper::registerUserHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
}
function tearDown()
{
\OC_FileProxy::clearProxies();
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
}
function testGenerateKey()
{
# TODO: use more accurate (larger) string length for test confirmation
$key = Encryption\Crypt::generateKey();
$this->assertTrue(strlen($key) > 16);
}
/**
* @return String
*/
function testGenerateIv()
{
$iv = Encryption\Crypt::generateIv();
$this->assertEquals(16, strlen($iv));
return $iv;
}
/**
* @depends testGenerateIv
*/
function testConcatIv($iv)
{
$catFile = Encryption\Crypt::concatIv($this->dataLong, $iv);
// Fetch encryption metadata from end of file
$meta = substr($catFile, -22);
$identifier = substr($meta, 0, 6);
// Fetch IV from end of file
$foundIv = substr($meta, 6);
$this->assertEquals('00iv00', $identifier);
$this->assertEquals($iv, $foundIv);
// Remove IV and IV identifier text to expose encrypted content
$data = substr($catFile, 0, -22);
$this->assertEquals($this->dataLong, $data);
return array(
'iv' => $iv
, 'catfile' => $catFile
);
}
/**
* @depends testConcatIv
*/
function testSplitIv($testConcatIv)
{
// Split catfile into components
$splitCatfile = Encryption\Crypt::splitIv($testConcatIv['catfile']);
// Check that original IV and split IV match
$this->assertEquals($testConcatIv['iv'], $splitCatfile['iv']);
// Check that original data and split data match
$this->assertEquals($this->dataLong, $splitCatfile['encrypted']);
}
/**
* @return string padded
*/
function testAddPadding()
{
$padded = Encryption\Crypt::addPadding($this->dataLong);
$padding = substr($padded, -2);
$this->assertEquals('xx', $padding);
return $padded;
}
/**
* @depends testAddPadding
*/
function testRemovePadding($padded)
{
$noPadding = Encryption\Crypt::RemovePadding($padded);
$this->assertEquals($this->dataLong, $noPadding);
}
function testEncrypt()
{
$random = openssl_random_pseudo_bytes(13);
$iv = substr(base64_encode($random), 0, -4); // i.e. E5IG033j+mRNKrht
$crypted = Encryption\Crypt::encrypt($this->dataUrl, $iv, 'hat');
$this->assertNotEquals($this->dataUrl, $crypted);
}
function testDecrypt()
{
$random = openssl_random_pseudo_bytes(13);
$iv = substr(base64_encode($random), 0, -4); // i.e. E5IG033j+mRNKrht
$crypted = Encryption\Crypt::encrypt($this->dataUrl, $iv, 'hat');
$decrypt = Encryption\Crypt::decrypt($crypted, $iv, 'hat');
$this->assertEquals($this->dataUrl, $decrypt);
}
function testSymmetricEncryptFileContent()
{
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$crypted = Encryption\Crypt::symmetricEncryptFileContent($this->dataShort, 'hat');
$this->assertNotEquals($this->dataShort, $crypted);
$decrypt = Encryption\Crypt::symmetricDecryptFileContent($crypted, 'hat');
$this->assertEquals($this->dataShort, $decrypt);
}
function testSymmetricStreamEncryptShortFileContent()
{
$filename = 'tmp-' . time() . '.test';
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// Check that the file was encrypted before being written to disk
$this->assertNotEquals($this->dataShort, $retreivedCryptedFile);
// Get the encrypted keyfile
$encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename);
// Attempt to fetch the user's shareKey
$shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename);
// get session
$session = new Encryption\Session($this->view);
// get private key
$privateKey = $session->getPrivateKey($this->userId);
// Decrypt keyfile with shareKey
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
// Manually decrypt
$manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent($retreivedCryptedFile, $plainKeyfile);
// Check that decrypted data matches
$this->assertEquals($this->dataShort, $manualDecrypt);
// Teardown
$this->view->unlink($this->userId . '/files/' . $filename);
Encryption\Keymanager::deleteFileKey($this->view, $this->userId, $filename);
}
/**
* @brief Test that data that is written by the crypto stream wrapper
* @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read
* @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
* reassembly of its data
*/
function testSymmetricStreamEncryptLongFileContent()
{
// Generate a a random filename
$filename = 'tmp-' . time() . '.test';
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong . $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Get file contents without using any wrapper to get it's actual contents on disk
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
// Check that the file was encrypted before being written to disk
$this->assertNotEquals($this->dataLong . $this->dataLong, $retreivedCryptedFile);
// Manuallly split saved file into separate IVs and encrypted chunks
$r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE);
//print_r($r);
// Join IVs and their respective data chunks
$e = array($r[0] . $r[1], $r[2] . $r[3], $r[4] . $r[5], $r[6] . $r[7], $r[8] . $r[9], $r[10] . $r[11]); //.$r[11], $r[12].$r[13], $r[14] );
//print_r($e);
// Get the encrypted keyfile
$encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename);
// Attempt to fetch the user's shareKey
$shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename);
// get session
$session = new Encryption\Session($this->view);
// get private key
$privateKey = $session->getPrivateKey($this->userId);
// Decrypt keyfile with shareKey
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
// Set var for reassembling decrypted content
$decrypt = '';
// Manually decrypt chunk
foreach ($e as $chunk) {
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent($chunk, $plainKeyfile);
// Assemble decrypted chunks
$decrypt .= $chunkDecrypt;
}
$this->assertEquals($this->dataLong . $this->dataLong, $decrypt);
// Teardown
$this->view->unlink($this->userId . '/files/' . $filename);
Encryption\Keymanager::deleteFileKey($this->view, $this->userId, $filename);
}
/**
* @brief Test that data that is read by the crypto stream wrapper
*/
function testSymmetricStreamDecryptShortFileContent()
{
$filename = 'tmp-' . time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$this->assertTrue(Encryption\Crypt::isEncryptedMeta($filename));
\OC_FileProxy::$enabled = $proxyStatus;
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataShort, $decrypt);
// tear down
$this->view->unlink($this->userId . '/files/' . $filename);
}
function testSymmetricStreamDecryptLongFileContent()
{
$filename = 'tmp-' . time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataLong, $decrypt);
// tear down
$this->view->unlink($this->userId . '/files/' . $filename);
}
function testSymmetricEncryptFileContentKeyfile()
{
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile($this->dataUrl);
$this->assertNotEquals($this->dataUrl, $crypted['encrypted']);
$decrypt = Encryption\Crypt::symmetricDecryptFileContent($crypted['encrypted'], $crypted['key']);
$this->assertEquals($this->dataUrl, $decrypt);
}
function testIsEncryptedContent()
{
$this->assertFalse(Encryption\Crypt::isCatfileContent($this->dataUrl));
$this->assertFalse(Encryption\Crypt::isCatfileContent($this->legacyEncryptedData));
$keyfileContent = Encryption\Crypt::symmetricEncryptFileContent($this->dataUrl, 'hat');
$this->assertTrue(Encryption\Crypt::isCatfileContent($keyfileContent));
}
function testMultiKeyEncrypt()
{
# TODO: search in keyfile for actual content as IV will ensure this test always passes
$pair1 = Encryption\Crypt::createKeypair();
$this->assertEquals(2, count($pair1));
$this->assertTrue(strlen($pair1['publicKey']) > 1);
$this->assertTrue(strlen($pair1['privateKey']) > 1);
$crypted = Encryption\Crypt::multiKeyEncrypt($this->dataShort, array($pair1['publicKey']));
$this->assertNotEquals($this->dataShort, $crypted['data']);
$decrypt = Encryption\Crypt::multiKeyDecrypt($crypted['data'], $crypted['keys'][0], $pair1['privateKey']);
$this->assertEquals($this->dataShort, $decrypt);
}
function testKeyEncrypt()
{
// Generate keypair
$pair1 = Encryption\Crypt::createKeypair();
// Encrypt data
$crypted = Encryption\Crypt::keyEncrypt($this->dataUrl, $pair1['publicKey']);
$this->assertNotEquals($this->dataUrl, $crypted);
// Decrypt data
$decrypt = Encryption\Crypt::keyDecrypt($crypted, $pair1['privateKey']);
$this->assertEquals($this->dataUrl, $decrypt);
}
/**
* @brief test encryption using legacy blowfish method
*/
function testLegacyEncryptShort()
{
$crypted = Encryption\Crypt::legacyEncrypt($this->dataShort, $this->pass);
$this->assertNotEquals($this->dataShort, $crypted);
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
return $crypted;
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptShort
*/
function testLegacyDecryptShort($crypted)
{
$decrypted = Encryption\Crypt::legacyDecrypt($crypted, $this->pass);
$this->assertEquals($this->dataShort, $decrypted);
}
/**
* @brief test encryption using legacy blowfish method
*/
function testLegacyEncryptLong()
{
$crypted = Encryption\Crypt::legacyEncrypt($this->dataLong, $this->pass);
$this->assertNotEquals($this->dataLong, $crypted);
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
return $crypted;
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptLong
*/
function testLegacyDecryptLong($crypted)
{
$decrypted = Encryption\Crypt::legacyDecrypt($crypted, $this->pass);
$this->assertEquals($this->dataLong, $decrypted);
$this->assertFalse(Encryption\Crypt::getBlowfish(''));
}
/**
* @brief test generation of legacy encryption key
* @depends testLegacyDecryptShort
*/
function testLegacyCreateKey()
{
// Create encrypted key
$encKey = Encryption\Crypt::legacyCreateKey($this->pass);
// Decrypt key
$key = Encryption\Crypt::legacyDecrypt($encKey, $this->pass);
$this->assertTrue(is_numeric($key));
// Check that key is correct length
$this->assertEquals(20, strlen($key));
}
/**
* @brief test decryption using legacy blowfish method
* @depends testLegacyEncryptLong
*/
function testLegacyKeyRecryptKeyfileEncrypt($crypted)
{
$recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile($crypted, $this->pass, array($this->genPublicKey), $this->pass, '');
$this->assertNotEquals($this->dataLong, $recrypted['data']);
return $recrypted;
# TODO: search inencrypted text for actual content to ensure it
# genuine transformation
}
function testRenameFile()
{
$filename = 'tmp-' . time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataLong, $decrypt);
$newFilename = 'tmp-new-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->rename($filename, $newFilename);
// Get file decrypted contents
$newDecrypt = file_get_contents('crypt://' . $newFilename);
$this->assertEquals($this->dataLong, $newDecrypt);
// tear down
$view->unlink($newFilename);
}
function testMoveFileIntoFolder()
{
$filename = 'tmp-' . time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataLong, $decrypt);
$newFolder = '/newfolder' . time();
$newFilename = 'tmp-new-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->mkdir($newFolder);
$view->rename($filename, $newFolder . '/' . $newFilename);
// Get file decrypted contents
$newDecrypt = file_get_contents('crypt://' . $newFolder . '/' . $newFilename);
$this->assertEquals($this->dataLong, $newDecrypt);
// tear down
$view->unlink($newFolder);
}
function testMoveFolder()
{
$view = new \OC\Files\View('/' . $this->userId . '/files');
$filename = '/tmp-' . time();
$folder = '/folder' . time();
$view->mkdir($folder);
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $folder . $filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $folder . $filename);
$this->assertEquals($this->dataLong, $decrypt);
$newFolder = '/newfolder/subfolder' . time();
$view->mkdir('/newfolder');
$view->rename($folder, $newFolder);
// Get file decrypted contents
$newDecrypt = file_get_contents('crypt://' . $newFolder . $filename);
$this->assertEquals($this->dataLong, $newDecrypt);
// tear down
$view->unlink($newFolder);
}
function testChangePassphrase()
{
$filename = 'tmp-' . time();
// Save long data as encrypted file using stream wrapper
$cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataLong, $decrypt);
// change password
\OC_User::setPassword($this->userId, 'test', null);
// relogin
$params['uid'] = $this->userId;
$params['password'] = 'test';
OCA\Encryption\Hooks::login($params);
// Get file decrypted contents
$newDecrypt = file_get_contents('crypt://' . $filename);
$this->assertEquals($this->dataLong, $newDecrypt);
// tear down
// change password back
\OC_User::setPassword($this->userId, $this->pass);
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->unlink($filename);
}
function testViewFilePutAndGetContents()
{
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = $view->file_get_contents($filename);
$this->assertEquals($this->dataShort, $decrypt);
// Save long data as encrypted file using stream wrapper
$cryptedFileLong = $view->file_put_contents($filename, $this->dataLong);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFileLong));
// Get file decrypted contents
$decryptLong = $view->file_get_contents($filename);
$this->assertEquals($this->dataLong, $decryptLong);
// tear down
$view->unlink($filename);
}
function testTouchExistingFile()
{
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$view->touch($filename);
// Get file decrypted contents
$decrypt = $view->file_get_contents($filename);
$this->assertEquals($this->dataShort, $decrypt);
// tear down
$view->unlink($filename);
}
function testTouchFile()
{
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
$view->touch($filename);
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// Get file decrypted contents
$decrypt = $view->file_get_contents($filename);
$this->assertEquals($this->dataShort, $decrypt);
// tear down
$view->unlink($filename);
}
function testFopenFile()
{
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$handle = $view->fopen($filename, 'r');
// Get file decrypted contents
$decrypt = fgets($handle);
$this->assertEquals($this->dataShort, $decrypt);
// tear down
$view->unlink($filename);
}
}

View File

@ -0,0 +1 @@
ÍÜä°E_cPï6HþV»sßà<1B>Êž

View File

@ -0,0 +1,245 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../lib/helper.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Keymanager
*/
class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase
{
public $userId;
public $pass;
public $stateFilesTrashbin;
/**
* @var OC_FilesystemView
*/
public $view;
public $randomKey;
public $dataShort;
function setUp()
{
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
\OC_FileProxy::$enabled = false;
// set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php'));
$this->dataShort = 'hats';
$this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php');
$this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt');
$this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt');
$this->randomKey = Encryption\Crypt::generateKey();
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView('/');
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
$userHome = \OC_User::getHome($this->userId);
$this->dataDir = str_replace('/' . $this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
}
function tearDown()
{
\OC_FileProxy::$enabled = true;
\OC_FileProxy::clearProxies();
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
}
function testGetPrivateKey()
{
$key = Encryption\Keymanager::getPrivateKey($this->view, $this->userId);
$privateKey = Encryption\Crypt::symmetricDecryptFileContent($key, $this->pass);
$res = openssl_pkey_get_private($privateKey);
$this->assertTrue(is_resource($res));
$sslInfo = openssl_pkey_get_details($res);
$this->assertArrayHasKey('key', $sslInfo);
}
function testGetPublicKey()
{
$publiceKey = Encryption\Keymanager::getPublicKey($this->view, $this->userId);
$res = openssl_pkey_get_public($publiceKey);
$this->assertTrue(is_resource($res));
$sslInfo = openssl_pkey_get_details($res);
$this->assertArrayHasKey('key', $sslInfo);
}
function testSetFileKey()
{
# NOTE: This cannot be tested until we are able to break out
# of the FileSystemView data directory root
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile($this->randomKey, 'hat');
$file = 'unittest-' . time() . '.txt';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
Encryption\Keymanager::setFileKey($this->view, $file, $this->userId, $key['key']);
// enable encryption proxy
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = true;
// cleanup
$this->view->unlink('/' . $this->userId . '/files/' . $file);
// change encryption proxy to previous state
\OC_FileProxy::$enabled = $proxyStatus;
}
function testGetUserKeys()
{
$keys = Encryption\Keymanager::getUserKeys($this->view, $this->userId);
$resPublic = openssl_pkey_get_public($keys['publicKey']);
$this->assertTrue(is_resource($resPublic));
$sslInfoPublic = openssl_pkey_get_details($resPublic);
$this->assertArrayHasKey('key', $sslInfoPublic);
$privateKey = Encryption\Crypt::symmetricDecryptFileContent($keys['privateKey'], $this->pass);
$resPrivate = openssl_pkey_get_private($privateKey);
$this->assertTrue(is_resource($resPrivate));
$sslInfoPrivate = openssl_pkey_get_details($resPrivate);
$this->assertArrayHasKey('key', $sslInfoPrivate);
}
function testFixPartialFilePath()
{
$partFilename = 'testfile.txt.part';
$filename = 'testfile.txt';
$this->assertTrue(Encryption\Keymanager::isPartialFilePath($partFilename));
$this->assertEquals('testfile.txt', Encryption\Keymanager::fixPartialFilePath($partFilename));
$this->assertFalse(Encryption\Keymanager::isPartialFilePath($filename));
$this->assertEquals('testfile.txt', Encryption\Keymanager::fixPartialFilePath($filename));
}
function testRecursiveDelShareKeys()
{
// generate filename
$filename = '/tmp-' . time() . '.txt';
// create folder structure
$this->view->mkdir('/admin/files/folder1');
$this->view->mkdir('/admin/files/folder1/subfolder');
$this->view->mkdir('/admin/files/folder1/subfolder/subsubfolder');
// enable encryption proxy
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = true;
// save file with content
$cryptedFile = file_put_contents('crypt:///folder1/subfolder/subsubfolder/' . $filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// change encryption proxy to previous state
\OC_FileProxy::$enabled = $proxyStatus;
// recursive delete keys
Encryption\Keymanager::delShareKey($this->view, array('admin'), '/folder1/');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/folder1/subfolder/subsubfolder/' . $filename . '.admin.shareKey'));
// enable encryption proxy
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = true;
// cleanup
$this->view->unlink('/admin/files/folder1');
// change encryption proxy to previous state
\OC_FileProxy::$enabled = $proxyStatus;
}
}

View File

@ -0,0 +1 @@
«ß• tÕ.µ¤—dS@t9 øQJ

View File

@ -52,7 +52,7 @@
// $this->userId = 'admin';
// $this->pass = 'admin';
//
// $this->session = new Encryption\Session();
// $this->session = new Encryption\Session( $view ); // FIXME: Provide a $view object for use here
//
// $this->session->setPrivateKey(
// '-----BEGIN PRIVATE KEY-----

View File

@ -0,0 +1,790 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php');
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../lib/helper.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Share
*/
class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
{
public $stateFilesTrashbin;
public $filename;
public $dataShort;
/**
* @var OC_FilesystemView
*/
public $view;
public $folder1;
public $subfolder;
public $subsubfolder;
function setUp()
{
// reset backend
\OC_User::clearBackends();
\OC_User::useBackend('database');
$this->dataShort = 'hats';
$this->view = new \OC_FilesystemView('/');
$userHome = \OC_User::getHome('admin');
$this->dataDir = str_replace('/admin', '', $userHome);
$this->folder1 = '/folder1';
$this->subfolder = '/subfolder1';
$this->subsubfolder = '/subsubfolder1';
$this->filename = 'share-tmp.test';
// enable resharing
\OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes');
// clear share hooks
\OC_Hook::clear('OCP\\Share');
\OC::registerShareHooks();
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
// Sharing related hooks
\OCA\Encryption\Helper::registerShareHooks();
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
// create users
$this->loginHelper('user1', true);
$this->loginHelper('user2', true);
$this->loginHelper('user3', true);
// create group and assign users
\OC_Group::createGroup('group1');
\OC_Group::addToGroup('user2', 'group1');
\OC_Group::addToGroup('user3', 'group1');
}
function tearDown()
{
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
// clean group
\OC_Group::deleteGroup('group1');
// cleanup users
\OC_User::deleteUser('user1');
\OC_User::deleteUser('user2');
\OC_User::deleteUser('user3');
\OC_FileProxy::clearProxies();
}
/**
* @param bool $withTeardown
*/
function testShareFile($withTeardown = true)
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user1 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// login as user1
$this->loginHelper('user1');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared/' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as admin
$this->loginHelper('admin');
// unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
}
/**
* @param bool $withTeardown
*/
function testReShareFile($withTeardown = true)
{
$this->testShareFile(false);
// login as user1
$this->loginHelper('user1');
// get the file info
$fileInfo = $this->view->getFileInfo('/user1/files/Shared/' . $this->filename);
// share the file with user2
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user2 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// login as user2
$this->loginHelper('user2');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename);
// check if data is the same as previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as user1
$this->loginHelper('user1');
// unshare the file with user2
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// login as admin
$this->loginHelper('admin');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// unshare the file with user1
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
}
/**
* @param bool $withTeardown
* @return array
*/
function testShareFolder($withTeardown = true)
{
// login as admin
$this->loginHelper('admin');
// create folder structure
$this->view->mkdir('/admin/files' . $this->folder1);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created folder
$fileInfo = $this->view->getFileInfo('/admin/files' . $this->folder1);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the folder with user1
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user1 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// login as user1
$this->loginHelper('user1');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as admin
$this->loginHelper('admin');
// unshare the folder with user1
\OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files' . $this->folder1);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
}
return $fileInfo;
}
/**
* @param bool $withTeardown
*/
function testReShareFolder($withTeardown = true)
{
$fileInfoFolder1 = $this->testShareFolder(false);
// login as user1
$this->loginHelper('user1');
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created folder
$fileInfoSubFolder = $this->view->getFileInfo('/user1/files/Shared' . $this->folder1 . $this->subfolder);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfoSubFolder));
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file with user2
\OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user2 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as user2
$this->loginHelper('user2');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// get the file info
$fileInfo = $this->view->getFileInfo('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if we have fileInfos
$this->assertTrue(is_array($fileInfo));
// share the file with user3
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user3 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user3
$this->loginHelper('user3');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user3/files/Shared/' . $this->filename);
// check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup
if ($withTeardown) {
// login as user2
$this->loginHelper('user2');
// unshare the file with user3
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user1
$this->loginHelper('user1');
// unshare the folder with user2
\OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as admin
$this->loginHelper('admin');
// unshare the folder1 with user1
\OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup
$this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
}
}
function testPublicShareFile()
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
$publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
// check if share key for public exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// some hacking to simulate public link
$GLOBALS['app'] = 'files_sharing';
$GLOBALS['fileOwner'] = 'admin';
\OC_User::setUserId('');
// get file contents
$retrievedCryptedFile = file_get_contents('crypt://' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// tear down
// login as admin
$this->loginHelper('admin');
// unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
function testShareFileWithGroup()
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, 'group1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user2 and user3 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user3.shareKey'));
// login as user1
$this->loginHelper('user2');
// get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
// login as admin
$this->loginHelper('admin');
// unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, 'group1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user3.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
}
function testRecoveryFile()
{
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
// login as admin
$this->loginHelper('admin');
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'admin');
// check if recovery password match
$this->assertTrue($util->checkRecoveryPassword('test123'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(1));
// create folder structure
$this->view->mkdir('/admin/files' . $this->folder1);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder);
$this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile1));
$this->assertTrue(is_int($cryptedFile2));
// check if share key for admin and recovery exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// disable recovery for admin
$this->assertTrue($util->setRecoveryForUser(0));
// remove all recovery keys
$util->removeRecoveryKeys('/');
// check if share key for recovery not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(1));
// remove all recovery keys
$util->addRecoveryKeys('/');
// check if share key for admin and recovery exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
$this->view->unlink('/admin/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key for recovery not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertTrue(\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123'));
$this->assertTrue(\OCA\Encryption\Helper::adminDisableRecovery('test123'));
$this->assertEquals(0, \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'));
}
function testRecoveryForUser()
{
// login as admin
$this->loginHelper('admin');
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
// login as user1
$this->loginHelper('user1');
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'user1');
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(1));
// create folder structure
$this->view->mkdir('/user1/files' . $this->folder1);
$this->view->mkdir('/user1/files' . $this->folder1 . $this->subfolder);
$this->view->mkdir('/user1/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile1));
$this->assertTrue(is_int($cryptedFile2));
// check if share key for user and recovery exists
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// login as admin
$this->loginHelper('admin');
// change password
\OC_User::setPassword('user1', 'test', 'test123');
// login as user1
$this->loginHelper('user1', false, 'test');
// get file contents
$retrievedCryptedFile1 = file_get_contents('crypt://' . $this->filename);
$retrievedCryptedFile2 = file_get_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile1);
$this->assertEquals($this->dataShort, $retrievedCryptedFile2);
// cleanup
$this->view->unlink('/user1/files' . $this->folder1);
$this->view->unlink('/user1/files' . $this->filename);
// check if share key for user and recovery exists
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(0));
\OCA\Encryption\Helper::adminDisableRecovery('test123');
$this->assertEquals(0, \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'));
}
function testFailShareFile()
{
// login as admin
$this->loginHelper('admin');
// save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info
$this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// break users public key
$this->view->rename('/public-keys/user2.public.key', '/public-keys/user2.public.key_backup');
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, 'group1', OCP\PERMISSION_ALL);
// login as admin
$this->loginHelper('admin');
// check if share key for user1 not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// break user1 public key
$this->view->rename('/public-keys/user2.public.key_backup', '/public-keys/user2.public.key');
// remove share file
$this->view->unlink('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey');
// re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus;
// unshare the file with user1
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, 'group1');
// check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey'));
// cleanup
$this->view->unlink('/admin/files/' . $this->filename);
}
/**
* @param $user
* @param bool $create
* @param bool $password
*/
function loginHelper($user, $create = false, $password = false)
{
if ($create) {
\OC_User::createUser($user, $user);
}
if ($password === false) {
$password = $user;
}
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($user);
\OC_User::setUserId($user);
$params['uid'] = $user;
$params['password'] = $password;
OCA\Encryption\Hooks::login($params);
}
}

View File

@ -0,0 +1,182 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Stream
* @brief this class provide basic stream tests
*/
class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase
{
public $userId;
public $pass;
/**
* @var \OC_FilesystemView
*/
public $view;
public $dataShort;
public $stateFilesTrashbin;
function setUp()
{
// reset backend
\OC_User::useBackend('database');
// set user id
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
// init filesystem view
$this->view = new \OC_FilesystemView('/');
// init short data
$this->dataShort = 'hats';
// init filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
// register encryption file proxy
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
// init filesystem for user
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
// login user
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
}
function tearDown()
{
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
// clear all proxies
\OC_FileProxy::clearProxies();
}
function testStreamOptions() {
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$handle = $view->fopen($filename, 'r');
// check if stream is at position zero
$this->assertEquals(0,ftell($handle));
// set stream options
$this->assertTrue(flock($handle, LOCK_SH));
$this->assertTrue(flock($handle, LOCK_UN));
// tear down
$view->unlink($filename);
}
function testStreamSetBlocking() {
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$handle = $view->fopen($filename, 'r');
// set stream options
$this->assertTrue(stream_set_blocking($handle,1));
// tear down
$view->unlink($filename);
}
function testStreamSetTimeout() {
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$handle = $view->fopen($filename, 'r');
// set stream options
$this->assertFalse(stream_set_timeout($handle,1));
// tear down
$view->unlink($filename);
}
function testStreamSetWriteBuffer() {
$filename = '/tmp-' . time();
$view = new \OC\Files\View('/' . $this->userId . '/files');
// Save short data as encrypted file using stream wrapper
$cryptedFile = $view->file_put_contents($filename, $this->dataShort);
// Test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
$handle = $view->fopen($filename, 'r');
// set stream options
$this->assertEquals(0, stream_set_write_buffer($handle,1024));
// tear down
$view->unlink($filename);
}
}

View File

@ -0,0 +1,269 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
require_once realpath(dirname(__FILE__) . '/../../files_trashbin/appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Trashbin
* @brief this class provide basic trashbin app tests
*/
class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase
{
public $userId;
public $pass;
/**
* @var \OC_FilesystemView
*/
public $view;
public $dataShort;
public $stateFilesTrashbin;
public $folder1;
public $subfolder;
public $subsubfolder;
function setUp()
{
// reset backend
\OC_User::useBackend('database');
// set user id
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
// init filesystem view
$this->view = new \OC_FilesystemView('/');
// init short data
$this->dataShort = 'hats';
$this->folder1 = '/folder1';
$this->subfolder = '/subfolder1';
$this->subsubfolder = '/subsubfolder1';
\OC_Hook::clear('OC_Filesystem');
\OC_Hook::clear('OC_User');
// init filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
// register encryption file proxy
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// trashbin hooks
\OCA\Files_Trashbin\Trashbin::registerHooks();
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::enable('files_trashbin');
// init filesystem for user
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
// login user
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
}
function tearDown()
{
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
// clear all proxies
\OC_FileProxy::clearProxies();
}
/**
* @brief test delete file
*/
function testDeleteFile() {
// generate filename
$filename = 'tmp-' . time() . '.txt';
// save file with content
$cryptedFile = file_put_contents('crypt:///' . $filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// check if key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key'));
// check if share key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey'));
// delete file
\OC\FIles\Filesystem::unlink($filename);
// check if file not exists
$this->assertFalse($this->view->file_exists('/admin/files/' . $filename));
// check if key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key'));
// check if share key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey'));
// get files
$trashFiles = $this->view->getDirectoryContent('/admin/files_trashbin/files/');
$trashFileSuffix = null;
// find created file with timestamp
foreach($trashFiles as $file) {
if(strncmp($file['path'], $filename, strlen($filename))) {
$path_parts = pathinfo($file['name']);
$trashFileSuffix = $path_parts['extension'];
}
}
// check if we found the file we created
$this->assertNotNull($trashFileSuffix);
// check if key for admin not exists
$this->assertTrue($this->view->file_exists('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix));
// check if share key for admin not exists
$this->assertTrue($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.shareKey.' . $trashFileSuffix));
// return filename for next test
return $filename . '.' . $trashFileSuffix;
}
/**
* @brief test restore file
*
* @depends testDeleteFile
*/
function testRestoreFile($filename) {
// prepare file information
$path_parts = pathinfo($filename);
$trashFileSuffix = $path_parts['extension'];
$timestamp = str_replace('d', '', $trashFileSuffix);
$fileNameWithoutSuffix = str_replace('.'.$trashFileSuffix, '', $filename);
// restore file
$this->assertTrue(\OCA\Files_Trashbin\Trashbin::restore($filename, $fileNameWithoutSuffix, $timestamp));
// check if file exists
$this->assertTrue($this->view->file_exists('/admin/files/' . $fileNameWithoutSuffix));
// check if key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/keyfiles/' . $fileNameWithoutSuffix . '.key'));
// check if share key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $fileNameWithoutSuffix . '.admin.shareKey'));
}
/**
* @brief test delete file forever
*/
function testPermanentDeleteFile() {
// generate filename
$filename = 'tmp-' . time() . '.txt';
// save file with content
$cryptedFile = file_put_contents('crypt:///' . $filename, $this->dataShort);
// test that data was successfully written
$this->assertTrue(is_int($cryptedFile));
// check if key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key'));
// check if share key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey'));
// delete file
\OC\FIles\Filesystem::unlink($filename);
// check if file not exists
$this->assertFalse($this->view->file_exists('/admin/files/' . $filename));
// check if key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key'));
// check if share key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey'));
// get files
$trashFiles = $this->view->getDirectoryContent('/admin/files_trashbin/files/');
$trashFileSuffix = null;
// find created file with timestamp
foreach($trashFiles as $file) {
if(strncmp($file['path'], $filename, strlen($filename))) {
$path_parts = pathinfo($file['name']);
$trashFileSuffix = $path_parts['extension'];
}
}
// check if we found the file we created
$this->assertNotNull($trashFileSuffix);
// check if key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix));
// check if share key for admin exists
$this->assertTrue($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.shareKey.' . $trashFileSuffix));
// get timestamp from file
$timestamp = str_replace('d', '', $trashFileSuffix);
// delete file forever
$this->assertGreaterThan(0, \OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp));
// check if key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_trashbin/files/' . $filename . '.' . $trashFileSuffix));
// check if key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix));
// check if share key for admin not exists
$this->assertFalse($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.shareKey.' . $trashFileSuffix));
}
}

View File

@ -0,0 +1,277 @@
<?php
/**
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Util
*/
class Test_Encryption_Util extends \PHPUnit_Framework_TestCase
{
public $userId;
public $encryptionDir;
public $publicKeyDir;
public $pass;
/**
* @var OC_FilesystemView
*/
public $view;
public $keyfilesPath;
public $publicKeyPath;
public $privateKeyPath;
/**
* @var \OCA\Encryption\Util
*/
public $util;
public $dataShort;
public $legacyEncryptedData;
public $legacyEncryptedDataKey;
public $lagacyKey;
function setUp()
{
// reset backend
\OC_User::useBackend('database');
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
// set content for encrypting / decrypting in tests
$this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php');
$this->dataShort = 'hats';
$this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php'));
$this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt');
$this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt');
$this->legacyEncryptedDataKey = realpath(dirname(__FILE__) . '/encryption.key');
$this->lagacyKey = '62829813025828180801';
$keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey'];
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
$this->view = new \OC_FilesystemView('/');
$userHome = \OC_User::getHome($this->userId);
$this->dataDir = str_replace('/' . $this->userId, '', $userHome);
// Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy());
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
$this->util = new Encryption\Util($this->view, $this->userId);
}
function tearDown()
{
\OC_FileProxy::clearProxies();
}
/**
* @brief test that paths set during User construction are correct
*/
function testKeyPaths()
{
$util = new Encryption\Util($this->view, $this->userId);
$this->assertEquals($this->publicKeyDir, $util->getPath('publicKeyDir'));
$this->assertEquals($this->encryptionDir, $util->getPath('encryptionDir'));
$this->assertEquals($this->keyfilesPath, $util->getPath('keyfilesPath'));
$this->assertEquals($this->publicKeyPath, $util->getPath('publicKeyPath'));
$this->assertEquals($this->privateKeyPath, $util->getPath('privateKeyPath'));
}
/**
* @brief test setup of encryption directories
*/
function testSetupServerSide()
{
$this->assertEquals(true, $this->util->setupServerSide($this->pass));
}
/**
* @brief test checking whether account is ready for encryption,
*/
function testUserIsReady()
{
$this->assertEquals(true, $this->util->ready());
}
/**
* @brief test checking whether account is not ready for encryption,
*/
function testUserIsNotReady()
{
$this->view->unlink($this->publicKeyDir);
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
$this->assertFalse(OCA\Encryption\Hooks::login($params));
$this->view->unlink($this->privateKeyPath);
}
/**
* @brief test checking whether account is not ready for encryption,
*/
function testIsLagacyUser()
{
$userView = new \OC_FilesystemView( '/' . $this->userId );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptionKeyContent = file_get_contents($this->legacyEncryptedDataKey);
$userView->file_put_contents('/encryption.key', $encryptionKeyContent);
\OC_FileProxy::$enabled = $proxyStatus;
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
$util = new Encryption\Util($this->view, $this->userId);
$util->setMigrationStatus(0);
$this->assertTrue(OCA\Encryption\Hooks::login($params));
$this->assertEquals($this->lagacyKey, $_SESSION['legacyKey']);
}
function testRecoveryEnabledForUser()
{
$util = new Encryption\Util($this->view, $this->userId);
// Record the value so we can return it to it's original state later
$enabled = $util->recoveryEnabledForUser();
$this->assertTrue($util->setRecoveryForUser(1));
$this->assertEquals(1, $util->recoveryEnabledForUser());
$this->assertTrue($util->setRecoveryForUser(0));
$this->assertEquals(0, $util->recoveryEnabledForUser());
// Return the setting to it's previous state
$this->assertTrue($util->setRecoveryForUser($enabled));
}
function testGetUidAndFilename()
{
\OC_User::setUserId('admin');
$filename = 'tmp-' . time() . '.test';
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
$util = new Encryption\Util($this->view, $this->userId);
list($fileOwnerUid, $file) = $util->getUidAndFilename($filename);
$this->assertEquals('admin', $fileOwnerUid);
$this->assertEquals($file, $filename);
}
function testIsSharedPath() {
$sharedPath = '/user1/files/Shared/test';
$path = '/user1/files/test';
$this->assertTrue($this->util->isSharedPath($sharedPath));
$this->assertFalse($this->util->isSharedPath($path));
}
function testEncryptLagacyFiles()
{
$userView = new \OC_FilesystemView( '/' . $this->userId);
$view = new \OC_FilesystemView( '/' . $this->userId . '/files' );
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptionKeyContent = file_get_contents($this->legacyEncryptedDataKey);
$userView->file_put_contents('/encryption.key', $encryptionKeyContent);
$legacyEncryptedData = file_get_contents($this->legacyEncryptedData);
$view->mkdir('/test/');
$view->mkdir('/test/subtest/');
$view->file_put_contents('/test/subtest/legacy-encrypted-text.txt', $legacyEncryptedData);
$fileInfo = $view->getFileInfo('/test/subtest/legacy-encrypted-text.txt');
$fileInfo['encrypted'] = true;
$view->putFileInfo('/test/subtest/legacy-encrypted-text.txt', $fileInfo);
\OC_FileProxy::$enabled = $proxyStatus;
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
$util = new Encryption\Util($this->view, $this->userId);
$util->setMigrationStatus(0);
$this->assertTrue(OCA\Encryption\Hooks::login($params));
$this->assertEquals($this->lagacyKey, $_SESSION['legacyKey']);
$files = $util->findEncFiles('/' . $this->userId . '/files/');
$this->assertTrue(is_array($files));
$found = false;
foreach($files['encrypted'] as $encryptedFile) {
if($encryptedFile['name'] === 'legacy-encrypted-text.txt') {
$found = true;
break;
}
}
$this->assertTrue($found);
}
}

View File

@ -0,0 +1,251 @@
<?php
/**
* ownCloud
*
* @author Florin Peter
* @copyright 2013 Florin Peter <owncloud@florin-peter.de>
*
* 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/>.
*
*/
require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption;
/**
* Class Test_Encryption_Webdav
* @brief this class provide basic webdav tests for PUT,GET and DELETE
*/
class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase
{
public $userId;
public $pass;
/**
* @var \OC_FilesystemView
*/
public $view;
public $dataShort;
public $stateFilesTrashbin;
function setUp()
{
// reset backend
\OC_User::useBackend('database');
// set user id
\OC_User::setUserId('admin');
$this->userId = 'admin';
$this->pass = 'admin';
// init filesystem view
$this->view = new \OC_FilesystemView('/');
// init short data
$this->dataShort = 'hats';
// init filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks();
// register encryption file proxy
\OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin');
// init filesystem for user
\OC_Util::tearDownFS();
\OC_User::setUserId('');
\OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId);
// login user
$params['uid'] = $this->userId;
$params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params);
}
function tearDown()
{
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
} else {
OC_App::disable('files_trashbin');
}
// clear all proxies
\OC_FileProxy::clearProxies();
}
/**
* @brief test webdav put random file
*/
function testWebdavPUT() {
// generate filename
$filename = '/tmp-' . time() . '.txt';
// set server vars
$_SERVER['REQUEST_METHOD'] = 'OPTIONS';
$_SERVER['REQUEST_METHOD'] = 'PUT';
$_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename;
$_SERVER['HTTP_AUTHORIZATION'] = 'Basic YWRtaW46YWRtaW4=';
$_SERVER['CONTENT_TYPE'] = 'application/octet-stream';
$_SERVER['PATH_INFO'] = '/webdav' . $filename;
$_SERVER['CONTENT_LENGTH'] = strlen($this->dataShort);
// handle webdav request
$this->handleWebdavRequest($this->dataShort);
// check if file was created
$this->assertTrue($this->view->file_exists('/' . $this->userId . '/files' . $filename));
// check if key-file was created
$this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/keyfiles/' . $filename . '.key'));
// check if shareKey-file was created
$this->assertTrue($this->view->file_exists('/' . $this->userId . '/files_encryption/share-keys/' . $filename . '.' . $this->userId . '.shareKey'));
// disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// get encrypted file content
$encryptedContent = $this->view->file_get_contents('/' . $this->userId . '/files' . $filename);
// restore proxy state
\OC_FileProxy::$enabled = $proxyStatus;
// check if encrypted content is valid
$this->assertTrue(Encryption\Crypt::isCatfileContent($encryptedContent));
// get decrypted file contents
$decrypt = file_get_contents('crypt://' . $filename);
// check if file content match with the written content
$this->assertEquals($this->dataShort, $decrypt);
// return filename for next test
return $filename;
}
/**
* @brief test webdav get random file
*
* @depends testWebdavPUT
*/
function testWebdavGET($filename) {
// set server vars
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename;
$_SERVER['HTTP_AUTHORIZATION'] = 'Basic YWRtaW46YWRtaW4=';
$_SERVER['PATH_INFO'] = '/webdav' . $filename;
// handle webdav request
$content = $this->handleWebdavRequest();
// check if file content match with the written content
$this->assertEquals($this->dataShort, $content);
// return filename for next test
return $filename;
}
/**
* @brief test webdav delete random file
* @depends testWebdavGET
*/
function testWebdavDELETE($filename) {
// set server vars
$_SERVER['REQUEST_METHOD'] = 'DELETE';
$_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename;
$_SERVER['HTTP_AUTHORIZATION'] = 'Basic YWRtaW46YWRtaW4=';
$_SERVER['PATH_INFO'] = '/webdav' . $filename;
// handle webdav request
$content = $this->handleWebdavRequest();
// check if file was removed
$this->assertFalse($this->view->file_exists('/' . $this->userId . '/files' . $filename));
// check if key-file was removed
$this->assertFalse($this->view->file_exists('/' . $this->userId . '/files_encryption/keyfiles' . $filename . '.key'));
// check if shareKey-file was removed
$this->assertFalse($this->view->file_exists('/' . $this->userId . '/files_encryption/share-keys' . $filename . '.' . $this->userId . '.shareKey'));
}
/**
* @brief handle webdav request
*
* @param bool $body
*
* @note this init procedure is copied from /apps/files/remote.php
*/
function handleWebdavRequest($body = false) {
// Backends
$authBackend = new OC_Connector_Sabre_Auth();
$lockBackend = new OC_Connector_Sabre_Locks();
$requestBackend = new OC_Connector_Sabre_Request();
// Create ownCloud Dir
$publicDir = new OC_Connector_Sabre_Directory('');
// Fire up server
$server = new Sabre_DAV_Server($publicDir);
$server->httpRequest = $requestBackend;
$server->setBaseUri('/remote.php/webdav/');
// Load plugins
$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud'));
$server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend));
$server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin());
$server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin());
// And off we go!
if($body) {
$server->httpRequest->setBody($body);
}
// turn on output buffering
ob_start();
// handle request
$server->exec();
// file content is written in the output buffer
$content = ob_get_contents();
// flush the output buffer and turn off output buffering
ob_end_clean();
// return captured content
return $content;
}
}

View File

@ -1,23 +1,4 @@
<?php $TRANSLATIONS = array(
"Access granted" => "Доступ разрешен",
"Error configuring Dropbox storage" => "Ошибка при конфигурировании хранилища Dropbox",
"Grant access" => "Предоставить доступ",
"Please provide a valid Dropbox app key and secret." => "Пожалуйста представьте допустимый ключ приложения Dropbox и пароль.",
"Error configuring Google Drive storage" => "Ошибка настройки хранилища Google Drive",
"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Предупреждение:</b> \"smbclient\" не установлен. Подключение общих папок CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.",
"<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Предупреждение:</b> Поддержка FTP в PHP не включена или не установлена. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить ее.",
"External Storage" => "Внешние системы хранения данных",
"Folder name" => "Имя папки",
"Configuration" => "Конфигурация",
"Options" => "Опции",
"Applicable" => "Применимый",
"None set" => "Не задан",
"All Users" => "Все пользователи",
"Groups" => "Группы",
"Users" => "Пользователи",
"Delete" => "Удалить",
"Enable User External Storage" => "Включить пользовательскую внешнюю систему хранения данных",
"Allow users to mount their own external storage" => "Разрешить пользователям монтировать их собственную внешнюю систему хранения данных",
"SSL root certificates" => "Корневые сертификаты SSL",
"Import Root Certificate" => "Импортировать корневые сертификаты"
"Delete" => "Удалить"
);

View File

@ -1,9 +1,3 @@
<?php $TRANSLATIONS = array(
"Password" => "Пароль",
"Submit" => "Передать",
"%s shared the folder %s with you" => "%s имеет общий с Вами доступ к папке %s ",
"%s shared the file %s with you" => "%s имеет общий с Вами доступ к файлу %s ",
"Download" => "Загрузка",
"No preview available for" => "Предварительный просмотр недоступен",
"web services under your control" => "веб-сервисы под Вашим контролем"
"Download" => "Загрузка"
);

View File

@ -3,7 +3,5 @@
OC::$CLASSPATH['OCA\Files_Trashbin\Hooks'] = 'files_trashbin/lib/hooks.php';
OC::$CLASSPATH['OCA\Files_Trashbin\Trashbin'] = 'files_trashbin/lib/trash.php';
//Listen to delete file signal
OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Trashbin\Hooks", "remove_hook");
//Listen to delete user signal
OCP\Util::connectHook('OC_User', 'pre_deleteUser', "OCA\Files_Trashbin\Hooks", "deleteUser_hook");
// register hooks
\OCA\Files_Trashbin\Trashbin::registerHooks();

View File

@ -13,6 +13,5 @@
"{count} files" => "{count} файла",
"Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!",
"Restore" => "Възтановяване",
"Delete" => "Изтриване",
"Deleted Files" => "Изтрити файлове"
"Delete" => "Изтриване"
);

View File

@ -2,13 +2,13 @@
"Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s",
"Couldn't restore %s" => "Tidak dapat memulihkan %s",
"perform restore operation" => "jalankan operasi pemulihan",
"Error" => "Galat",
"Error" => "kesalahan",
"delete file permanently" => "hapus berkas secara permanen",
"Delete permanently" => "Hapus secara permanen",
"Delete permanently" => "hapus secara permanen",
"Name" => "Nama",
"Deleted" => "Dihapus",
"1 folder" => "1 folder",
"{count} folders" => "{count} folder",
"1 folder" => "1 map",
"{count} folders" => "{count} map",
"1 file" => "1 berkas",
"{count} files" => "{count} berkas",
"Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!",

View File

@ -8,9 +8,9 @@
"Name" => "Nazwa",
"Deleted" => "Usunięte",
"1 folder" => "1 folder",
"{count} folders" => "Ilość folderów: {count}",
"{count} folders" => "{count} foldery",
"1 file" => "1 plik",
"{count} files" => "Ilość plików: {count}",
"{count} files" => "{count} pliki",
"Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!",
"Restore" => "Przywróć",
"Delete" => "Usuń",

View File

@ -13,6 +13,6 @@
"{count} files" => "{count} ficheiros",
"Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!",
"Restore" => "Restaurar",
"Delete" => "Eliminar",
"Delete" => "Apagar",
"Deleted Files" => "Ficheiros Apagados"
);

View File

@ -1,6 +1,5 @@
<?php $TRANSLATIONS = array(
"Error" => "Eroare",
"Delete permanently" => "Stergere permanenta",
"Name" => "Nume",
"1 folder" => "1 folder",
"{count} folders" => "{count} foldare",

View File

@ -1,3 +1,5 @@
<?php $TRANSLATIONS = array(
"Error" => "Ошибка"
"Error" => "Ошибка",
"Name" => "Имя",
"Delete" => "Удалить"
);

View File

@ -5,7 +5,7 @@
"Error" => "Chyba",
"delete file permanently" => "trvalo zmazať súbor",
"Delete permanently" => "Zmazať trvalo",
"Name" => "Názov",
"Name" => "Meno",
"Deleted" => "Zmazané",
"1 folder" => "1 priečinok",
"{count} folders" => "{count} priečinkov",

View File

@ -29,6 +29,17 @@ class Trashbin {
// unit: percentage; 50% of available disk space/quota
const DEFAULTMAXSIZE=50;
public static function getUidAndFilename($filename) {
$uid = \OC\Files\Filesystem::getOwner($filename);
\OC\Files\Filesystem::initMountPoints($uid);
if ( $uid != \OCP\User::getUser() ) {
$info = \OC\Files\Filesystem::getFileInfo($filename);
$ownerView = new \OC\Files\View('/'.$uid.'/files');
$filename = $ownerView->getPath($info['fileid']);
}
return array($uid, $filename);
}
/**
* move file to the trash bin
*
@ -62,8 +73,12 @@ class Trashbin {
if ( $trashbinSize === false || $trashbinSize < 0 ) {
$trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin'));
}
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view);
\OC_FileProxy::$enabled = $proxyStatus;
if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) {
$trashbinSize += $sizeOfAddedFiles;
@ -110,13 +125,17 @@ class Trashbin {
\OC_FileProxy::$enabled = false;
$user = \OCP\User::getUser();
if ($view->is_dir('files_versions/' . $file_path)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_versions/' . $file_path));
$view->rename('files_versions/' . $file_path, 'files_trashbin/versions/' . $filename . '.d' . $timestamp);
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path)) {
$rootView = new \OC\Files\View('/');
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
if ($rootView->is_dir($owner.'/files_versions/' . $ownerPath)) {
$size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath));
$rootView->rename($owner.'/files_versions/' . $ownerPath, $user.'/files_trashbin/versions/' . $filename . '.d' . $timestamp);
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
foreach ($versions as $v) {
$size += $view->filesize('files_versions' . $v['path'] . '.v' . $v['version']);
$view->rename('files_versions' . $v['path'] . '.v' . $v['version'], 'files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
$size += $rootView->filesize($owner.'/files_versions' . $v['path'] . '.v' . $v['version']);
$rootView->rename($owner.'/files_versions' . $v['path'] . '.v' . $v['version'], $user.'/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
}
}
@ -143,35 +162,38 @@ class Trashbin {
if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
list($owner, $ownerPath) = self::getUidAndFilename($file_path);
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// retain key files
$keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/' . $file_path);
$keyfile = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/keyfiles/' . $ownerPath);
if ($view->is_dir($keyfile) || $view->file_exists($keyfile . '.key')) {
$user = \OCP\User::getUser();
if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) {
// move keyfiles
if ($view->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$view->rename($keyfile, 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
if ($rootView->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View($keyfile));
$rootView->rename($keyfile, $user.'/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
} else {
$size += $view->filesize($keyfile . '.key');
$view->rename($keyfile . '.key', 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
$size += $rootView->filesize($keyfile . '.key');
$rootView->rename($keyfile . '.key', $user.'/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp);
}
}
// retain share keys
$sharekeys = \OC\Files\Filesystem::normalizePath('files_encryption/share-keys/' . $file_path);
$sharekeys = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/share-keys/' . $ownerPath);
if ($view->is_dir($sharekeys)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
$view->rename($sharekeys, 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
if ($rootView->is_dir($sharekeys)) {
$size += self::calculateSize(new \OC\Files\View($sharekeys));
$rootView->rename($sharekeys, $user.'/files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
} else {
// get local path to share-keys
$localShareKeysPath = $view->getLocalFile($sharekeys);
$localShareKeysPath = $rootView->getLocalFile($sharekeys);
// handle share-keys
$matches = glob(preg_quote($localShareKeysPath).'*.shareKey');
@ -186,10 +208,10 @@ class Trashbin {
if($pathinfo['basename'] == $ownerShareKey) {
// calculate size
$size += $view->filesize($sharekeys. '.' . $user. '.shareKey');
$size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey');
// move file
$view->rename($sharekeys. '.' . $user. '.shareKey', 'files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp);
$rootView->rename($sharekeys. '.' . $user. '.shareKey', $user.'/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp);
} else {
// calculate size
@ -321,6 +343,12 @@ class Trashbin {
\OC_FileProxy::$enabled = false;
$user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
list($owner, $ownerPath) = self::getUidAndFilename($target);
if ($timestamp) {
$versionedFile = $filename;
} else {
@ -329,15 +357,15 @@ class Trashbin {
if ($view->is_dir('/files_trashbin/versions/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file));
$view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath('files_versions/' . $location . '/' . $filename . $ext));
$rootView->rename(\OC\Files\Filesystem::normalizePath($user.'/files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath($owner.'/files_versions/' . $ownerPath));
} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) {
foreach ($versions as $v) {
if ($timestamp) {
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp);
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v);
$rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
} else {
$size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v);
$view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v);
$rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner.'/files_versions/' . $ownerPath . '.v' . $v);
}
}
}
@ -356,7 +384,7 @@ class Trashbin {
* @param $file complete path to file
* @param $filename name of file
* @param $ext file extension in case a file with the same $filename already exists
* @param $location location if file
* @param $location location of file
* @param $timestamp deleteion time
*
* @return size of restored encrypted file
@ -366,20 +394,25 @@ class Trashbin {
$size = 0;
if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser();
$rootView = new \OC\Files\View('/');
$target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext);
list($owner, $ownerPath) = self::getUidAndFilename($target);
$path_parts = pathinfo($file);
$source_location = $path_parts['dirname'];
if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) {
if($source_location != '.') {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename);
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename);
} else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $filename);
$sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $filename);
}
} else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key');
$keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key');
}
if ($timestamp) {
@ -390,35 +423,36 @@ class Trashbin {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($view->file_exists($keyfile)) {
if ($rootView->file_exists($keyfile)) {
// handle directory
if ($view->is_dir($keyfile)) {
if ($rootView->is_dir($keyfile)) {
// handle keyfiles
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext);
$size += self::calculateSize(new \OC\Files\View($keyfile));
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath);
// handle share-keys
if ($timestamp) {
$sharekey .= '.d' . $timestamp;
}
$view->rename($sharekey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext);
$size += self::calculateSize(new \OC\Files\View($sharekey));
$rootView->rename($sharekey, $owner.'/files_encryption/share-keys/' . $ownerPath);
} else {
// handle keyfiles
$size += $view->filesize($keyfile);
$view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key');
$size += $rootView->filesize($keyfile);
$rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key');
// handle share-keys
$ownerShareKey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey');
$ownerShareKey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey');
if ($timestamp) {
$ownerShareKey .= '.d' . $timestamp;
}
$size += $view->filesize($ownerShareKey);
$size += $rootView->filesize($ownerShareKey);
// move only owners key
$view->rename($ownerShareKey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext . '.' . $user. '.shareKey');
$rootView->rename($ownerShareKey, $owner.'/files_encryption/share-keys/' . $ownerPath . '.' . $user. '.shareKey');
// try to re-share if file is shared
$filesystemView = new \OC_FilesystemView('/');
@ -426,7 +460,7 @@ class Trashbin {
$util = new \OCA\Encryption\Util($filesystemView, $user);
// fix the file size
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $user . '/files/'. $location. '/' .$filename);
$absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath);
$util->fixFileSize($absolutePath);
// get current sharing state
@ -475,7 +509,25 @@ class Trashbin {
$file = $filename;
}
$size += self::deleteVersions($view, $file, $filename, $timestamp);
$size += self::deleteEncryptionKeys($view, $file, $filename, $timestamp);
if ($view->is_dir('/files_trashbin/files/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
} else {
$size += $view->filesize('/files_trashbin/files/'.$file);
}
$view->unlink('/files_trashbin/files/'.$file);
$trashbinSize -= $size;
self::setTrashbinSize($user, $trashbinSize);
return $size;
}
private static function deleteVersions($view, $file, $filename, $timestamp) {
$size = 0;
if ( \OCP\App::isEnabled('files_versions') ) {
$user = \OCP\User::getUser();
if ($view->is_dir('files_trashbin/versions/'.$file)) {
$size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file));
$view->unlink('files_trashbin/versions/'.$file);
@ -491,35 +543,37 @@ class Trashbin {
}
}
}
// Take care of encryption keys
$parts = pathinfo($file);
if ( $view->is_dir('/files_trashbin/files/'.$file) ) {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename);
} else {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename.'.key');
}
if ($timestamp) {
$keyfile .= '.d'.$timestamp;
}
if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) {
if ( $view->is_dir($keyfile) ) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile));
return $size;
}
private static function deleteEncryptionKeys($view, $file, $filename, $timestamp) {
$size = 0;
if (\OCP\App::isEnabled('files_encryption')) {
$user = \OCP\User::getUser();
if ($view->is_dir('/files_trashbin/files/' . $file)) {
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename);
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename);
} else {
$size += $view->filesize($keyfile);
$keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key');
$sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename . '.' . $user . '.shareKey');
}
if ($timestamp) {
$keyfile .= '.d' . $timestamp;
$sharekeys .= '.d' . $timestamp;
}
if ($view->file_exists($keyfile)) {
if ($view->is_dir($keyfile)) {
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile));
$size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys));
} else {
$size += $view->filesize($keyfile);
$size += $view->filesize($sharekeys);
}
$view->unlink($keyfile);
$view->unlink($sharekeys);
}
$view->unlink($keyfile);
}
if ($view->is_dir('/files_trashbin/files/'.$file)) {
$size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file));
} else {
$size += $view->filesize('/files_trashbin/files/'.$file);
}
$view->unlink('/files_trashbin/files/'.$file);
$trashbinSize -= $size;
self::setTrashbinSize($user, $trashbinSize);
return $size;
}
@ -779,5 +833,14 @@ class Trashbin {
}
$query->execute(array($size, $user));
}
/**
* register hooks
*/
public static function registerHooks() {
//Listen to delete file signal
\OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Trashbin\Hooks", "remove_hook");
//Listen to delete user signal
\OCP\Util::connectHook('OC_User', 'pre_deleteUser', "OCA\Files_Trashbin\Hooks", "deleteUser_hook");
}
}

View File

@ -73,6 +73,7 @@
"User Home Folder Naming Rule" => "Benennungsregel für das Home-Verzeichnis des Benutzers",
"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfalls tragen Sie bitte ein LDAP/AD-Attribut ein.",
"Internal Username" => "Interner Benutzername",
"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Standardmäßig wird der interne Benutzername mittels des UUID-Attributes erzeugt. Dies stellt sicher, dass der Benutzername einzigartig ist und keinerlei Zeichen konvertiert werden müssen. Der interne Benutzername unterliegt Beschränkungen, die nur die nachfolgenden Zeichen erlauben: [ a-zA-Z0-9_.@- ]. Andere Zeichenwerden mittels ihrer korrespondierenden Zeichen ersetzt oder einfach ausgelassen. Bei Übereinstimmungen wird ein Zähler hinzugefügt bzw. der Zähler um einen Wert erhöht. Der interne Benutzername wird benutzt, um einen Benutzer intern zu identifizieren. Es ist ebenso der standardmäßig vorausgewählte Namen des Heimatverzeichnisses in ownCloud. Es dient weiterhin als Port für Remote-URLs - zum Beispiel für alle *DAV-Dienste Mit dieser Einstellung kann das Standardverhalten überschrieben werden. Um ein ähnliches Verhalten wie vor ownCloud 5 zu erzielen, fügen Sie das anzuzeigende Attribut des Benutzernamens in das nachfolgende Feld ein. Lassen Sie dies hingegen für das Standardverhalten leer. Die Änderungen werden sich einzig und allein nur auf neu gemappte (hinzugefügte) LDAP-Benutzer auswirken.",
"Test Configuration" => "Testkonfiguration",
"Help" => "Hilfe"
);

View File

@ -1,16 +1,16 @@
<?php $TRANSLATIONS = array(
"Failed to delete the server configuration" => "Sunucu uyunlama basarmadi ",
"The configuration is valid and the connection could be established!" => "Uyunlama mantikli ve baglama yerlestirmek edebilmi.",
"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Uyunlama gecerli, fakat Baglama yapamadi. Lutfen kontrol yapmak, eger bu iyi yerlertirdi. ",
"The configuration is invalid. Please look in the ownCloud log for further details." => "Uyunma mantikli degil. Lutfen log daha kontrol yapmak. ",
"Failed to delete the server configuration" => "Sunucu yapılandırmasını silme başarısız oldu",
"The configuration is valid and the connection could be established!" => "Yapılandırma geçerli ve bağlantı kuruldu!",
"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Yapılandırma geçerli fakat bağlanma(bind) başarısız. Lütfen Sunucu ayarları ve kimlik bilgilerini kontrol ediniz.",
"The configuration is invalid. Please look in the ownCloud log for further details." => "Yapılandırma geçersiz. Daha fazla detay için lütfen ownCloud günlüklerine bakınız.",
"Deletion failed" => "Silme başarısız oldu",
"Take over settings from recent server configuration?" => "Parametri sonadan uyunlama cikarmak mi?",
"Keep settings?" => "Ayarları kalsınmı?",
"Cannot add server configuration" => "Sunucu uyunlama birlemek edemen. ",
"Take over settings from recent server configuration?" => "Ayarları son sunucu yapılandırmalarından devral?",
"Keep settings?" => "Ayarlar kalsın mı?",
"Cannot add server configuration" => "Sunucu yapılandırması eklenemedi",
"Error" => "Hata",
"Connection test succeeded" => "Bağlantı testi başarılı oldu",
"Connection test failed" => "Bağlantı testi başarısız oldu",
"Do you really want to delete the current Server Configuration?" => "Hakikatten, Sonuncu Funksyon durmak istiyor mi?",
"Do you really want to delete the current Server Configuration?" => "Şu anki sunucu yapılandırmasını silmek istediğinizden emin misiniz?",
"Confirm Deletion" => "Silmeyi onayla",
"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Uyari </b> Apps kullanici_Idap ve user_webdavauth uyunmayan. Bu belki sik degil. Lutfen sistem yonetici sormak on aktif yapmaya. ",
"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Ihbar <b> Modulu PHP LDAP yuklemdi degil, backend calismacak. Lutfen sistem yonetici sormak yuklemek icin.",

View File

@ -54,6 +54,22 @@ cat > ./tests/autoconfig-pgsql.php <<DELIM
);
DELIM
cat > ./tests/autoconfig-oci.php <<DELIM
<?php
\$AUTOCONFIG = array (
'installed' => false,
'dbtype' => 'oci',
'dbtableprefix' => 'oc_',
'adminlogin' => 'admin',
'adminpass' => 'admin',
'directory' => '$BASEDIR/$DATADIR',
'dbuser' => 'oc_autotest',
'dbname' => 'XE',
'dbhost' => 'localhost',
'dbpass' => 'owncloud',
);
DELIM
function execute_tests {
echo "Setup environment for $1 testing ..."
# back to root folder
@ -77,6 +93,30 @@ function execute_tests {
if [ "$1" == "pgsql" ] ; then
dropdb -U oc_autotest oc_autotest
fi
if [ "$1" == "oci" ] ; then
echo "drop the database"
sqlplus -s -l / as sysdba <<EOF
drop user oc_autotest cascade;
EOF
echo "create the database"
sqlplus -s -l / as sysdba <<EOF
create user oc_autotest identified by owncloud;
alter user oc_autotest default tablespace users
temporary tablespace temp
quota unlimited on users;
grant create session
, create table
, create procedure
, create sequence
, create trigger
, create view
, create synonym
, alter session
to oc_autotest;
exit;
EOF
fi
# copy autoconfig
cp $BASEDIR/tests/autoconfig-$1.php $BASEDIR/config/autoconfig.php
@ -101,9 +141,16 @@ function execute_tests {
#
# start test execution
#
execute_tests "sqlite"
execute_tests 'mysql'
execute_tests 'pgsql'
if [ -z "$1" ]
then
execute_tests "sqlite"
execute_tests 'mysql'
execute_tests 'pgsql'
# we will add oci as soon as it's stable
#execute_tests 'oci'
else
execute_tests $1
fi
#
# NOTES on mysql:
@ -116,4 +163,8 @@ execute_tests 'pgsql'
# - to enable dropdb I decided to add following line to pg_hba.conf (this is not the safest way but I don't care for the testing machine):
# local all all trust
#
# NOTES on oci:
# - it's a pure nightmare to install Oracle on a Linux-System
# - DON'T TRY THIS AT HOME!
# - if you really need it: we feel sorry for you
#

View File

@ -44,11 +44,11 @@
"months ago" => "شهر مضى",
"last year" => "السنةالماضية",
"years ago" => "سنة مضت",
"Ok" => "موافق",
"Cancel" => "الغاء",
"Choose" => "اختيار",
"Cancel" => "الغاء",
"Yes" => "نعم",
"No" => "لا",
"Ok" => "موافق",
"The object type is not specified." => "نوع العنصر غير محدد.",
"Error" => "خطأ",
"The app name is not specified." => "اسم التطبيق غير محدد.",

View File

@ -28,10 +28,10 @@
"last month" => "последният месец",
"last year" => "последната година",
"years ago" => "последните години",
"Ok" => "Добре",
"Cancel" => "Отказ",
"Yes" => "Да",
"No" => "Не",
"Ok" => "Добре",
"Error" => "Грешка",
"Share" => "Споделяне",
"Share with" => "Споделено с",

View File

@ -43,11 +43,11 @@
"months ago" => "মাস পূর্বে",
"last year" => "গত বছর",
"years ago" => "বছর পূর্বে",
"Ok" => "তথাস্তু",
"Cancel" => "বাতির",
"Choose" => "বেছে নিন",
"Cancel" => "বাতির",
"Yes" => "হ্যাঁ",
"No" => "না",
"Ok" => "তথাস্তু",
"The object type is not specified." => "অবজেক্টের ধরণটি সুনির্দিষ্ট নয়।",
"Error" => "সমস্যা",
"The app name is not specified." => "অ্যাপের নামটি সুনির্দিষ্ট নয়।",

View File

@ -44,11 +44,11 @@
"months ago" => "mesos enrere",
"last year" => "l'any passat",
"years ago" => "anys enrere",
"Ok" => "D'acord",
"Cancel" => "Cancel·la",
"Choose" => "Escull",
"Cancel" => "Cancel·la",
"Yes" => "",
"No" => "No",
"Ok" => "D'acord",
"The object type is not specified." => "No s'ha especificat el tipus d'objecte.",
"Error" => "Error",
"The app name is not specified." => "No s'ha especificat el nom de l'aplicació.",

View File

@ -44,11 +44,11 @@
"months ago" => "před měsíci",
"last year" => "minulý rok",
"years ago" => "před lety",
"Ok" => "Ok",
"Cancel" => "Zrušit",
"Choose" => "Vybrat",
"Cancel" => "Zrušit",
"Yes" => "Ano",
"No" => "Ne",
"Ok" => "Ok",
"The object type is not specified." => "Není určen typ objektu.",
"Error" => "Chyba",
"The app name is not specified." => "Není určen název aplikace.",

View File

@ -44,11 +44,11 @@
"months ago" => "misoedd yn ôl",
"last year" => "y llynedd",
"years ago" => "blwyddyn yn ôl",
"Ok" => "Iawn",
"Cancel" => "Diddymu",
"Choose" => "Dewisiwch",
"Cancel" => "Diddymu",
"Yes" => "Ie",
"No" => "Na",
"Ok" => "Iawn",
"The object type is not specified." => "Nid yw'r math o wrthrych wedi cael ei nodi.",
"Error" => "Gwall",
"The app name is not specified." => "Nid yw enw'r pecyn wedi cael ei nodi.",

View File

@ -44,11 +44,11 @@
"months ago" => "måneder siden",
"last year" => "sidste år",
"years ago" => "år siden",
"Ok" => "OK",
"Cancel" => "Annuller",
"Choose" => "Vælg",
"Cancel" => "Annuller",
"Yes" => "Ja",
"No" => "Nej",
"Ok" => "OK",
"The object type is not specified." => "Objekttypen er ikke angivet.",
"Error" => "Fejl",
"The app name is not specified." => "Den app navn er ikke angivet.",

View File

@ -44,11 +44,11 @@
"months ago" => "Vor Monaten",
"last year" => "Letztes Jahr",
"years ago" => "Vor Jahren",
"Ok" => "OK",
"Cancel" => "Abbrechen",
"Choose" => "Auswählen",
"Cancel" => "Abbrechen",
"Yes" => "Ja",
"No" => "Nein",
"Ok" => "OK",
"The object type is not specified." => "Der Objekttyp ist nicht angegeben.",
"Error" => "Fehler",
"The app name is not specified." => "Der App-Name ist nicht angegeben.",

View File

@ -44,11 +44,11 @@
"months ago" => "Vor Monaten",
"last year" => "Letztes Jahr",
"years ago" => "Vor Jahren",
"Ok" => "OK",
"Cancel" => "Abbrechen",
"Choose" => "Auswählen",
"Cancel" => "Abbrechen",
"Yes" => "Ja",
"No" => "Nein",
"Ok" => "OK",
"The object type is not specified." => "Der Objekttyp ist nicht angegeben.",
"Error" => "Fehler",
"The app name is not specified." => "Der App-Name ist nicht angegeben.",

View File

@ -44,11 +44,11 @@
"months ago" => "μήνες πριν",
"last year" => "τελευταίο χρόνο",
"years ago" => "χρόνια πριν",
"Ok" => "Οκ",
"Cancel" => "Άκυρο",
"Choose" => "Επιλέξτε",
"Cancel" => "Άκυρο",
"Yes" => "Ναι",
"No" => "Όχι",
"Ok" => "Οκ",
"The object type is not specified." => "Δεν καθορίστηκε ο τύπος του αντικειμένου.",
"Error" => "Σφάλμα",
"The app name is not specified." => "Δεν καθορίστηκε το όνομα της εφαρμογής.",

View File

@ -43,11 +43,11 @@
"months ago" => "monatoj antaŭe",
"last year" => "lastajare",
"years ago" => "jaroj antaŭe",
"Ok" => "Akcepti",
"Cancel" => "Nuligi",
"Choose" => "Elekti",
"Cancel" => "Nuligi",
"Yes" => "Jes",
"No" => "Ne",
"Ok" => "Akcepti",
"The object type is not specified." => "Ne indikiĝis tipo de la objekto.",
"Error" => "Eraro",
"The app name is not specified." => "Ne indikiĝis nomo de la aplikaĵo.",

View File

@ -44,11 +44,11 @@
"months ago" => "hace meses",
"last year" => "el año pasado",
"years ago" => "hace años",
"Ok" => "Aceptar",
"Cancel" => "Cancelar",
"Choose" => "Seleccionar",
"Cancel" => "Cancelar",
"Yes" => "",
"No" => "No",
"Ok" => "Aceptar",
"The object type is not specified." => "No se ha especificado el tipo de objeto",
"Error" => "Error",
"The app name is not specified." => "No se ha especificado el nombre de la aplicación.",

View File

@ -44,11 +44,11 @@
"months ago" => "meses atrás",
"last year" => "el año pasado",
"years ago" => "años atrás",
"Ok" => "Aceptar",
"Cancel" => "Cancelar",
"Choose" => "Elegir",
"Cancel" => "Cancelar",
"Yes" => "",
"No" => "No",
"Ok" => "Aceptar",
"The object type is not specified." => "El tipo de objeto no esta especificado. ",
"Error" => "Error",
"The app name is not specified." => "El nombre de la aplicación no esta especificado.",

View File

@ -44,11 +44,11 @@
"months ago" => "kuu tagasi",
"last year" => "viimasel aastal",
"years ago" => "aastat tagasi",
"Ok" => "Ok",
"Cancel" => "Loobu",
"Choose" => "Vali",
"Cancel" => "Loobu",
"Yes" => "Jah",
"No" => "Ei",
"Ok" => "Ok",
"The object type is not specified." => "Objekti tüüp pole määratletud.",
"Error" => "Viga",
"The app name is not specified." => "Rakenduse nimi ole määratletud",

View File

@ -44,11 +44,11 @@
"months ago" => "hilabete",
"last year" => "joan den urtean",
"years ago" => "urte",
"Ok" => "Ados",
"Cancel" => "Ezeztatu",
"Choose" => "Aukeratu",
"Cancel" => "Ezeztatu",
"Yes" => "Bai",
"No" => "Ez",
"Ok" => "Ados",
"The object type is not specified." => "Objetu mota ez dago zehaztuta.",
"Error" => "Errorea",
"The app name is not specified." => "App izena ez dago zehaztuta.",

View File

@ -44,11 +44,11 @@
"months ago" => "ماه‌های قبل",
"last year" => "سال قبل",
"years ago" => "سال‌های قبل",
"Ok" => "قبول",
"Cancel" => "منصرف شدن",
"Choose" => "انتخاب کردن",
"Cancel" => "منصرف شدن",
"Yes" => "بله",
"No" => "نه",
"Ok" => "قبول",
"The object type is not specified." => "نوع شی تعیین نشده است.",
"Error" => "خطا",
"The app name is not specified." => "نام برنامه تعیین نشده است.",

View File

@ -42,11 +42,11 @@
"months ago" => "kuukautta sitten",
"last year" => "viime vuonna",
"years ago" => "vuotta sitten",
"Ok" => "Ok",
"Cancel" => "Peru",
"Choose" => "Valitse",
"Cancel" => "Peru",
"Yes" => "Kyllä",
"No" => "Ei",
"Ok" => "Ok",
"Error" => "Virhe",
"The app name is not specified." => "Sovelluksen nimeä ei ole määritelty.",
"The required file {file} is not installed!" => "Vaadittua tiedostoa {file} ei ole asennettu!",
@ -84,6 +84,7 @@
"The update was successful. Redirecting you to ownCloud now." => "Päivitys onnistui. Selain ohjautuu nyt ownCloudiisi.",
"ownCloud password reset" => "ownCloud-salasanan nollaus",
"Use the following link to reset your password: {link}" => "Voit palauttaa salasanasi seuraavassa osoitteessa: {link}",
"Request failed!<br>Did you make sure your email/username was right?" => "Pyyntö epäonnistui!<br>Olihan sähköpostiosoitteesi/käyttäjätunnuksesi oikein?",
"You will receive a link to reset your password via Email." => "Saat sähköpostitse linkin nollataksesi salasanan.",
"Username" => "Käyttäjätunnus",
"Request reset" => "Tilaus lähetetty",
@ -118,6 +119,7 @@
"Database host" => "Tietokantapalvelin",
"Finish setup" => "Viimeistele asennus",
"web services under your control" => "verkkopalvelut hallinnassasi",
"%s is available. Get more information on how to update." => "%s on saatavilla. Lue lisätietoja, miten päivitys asennetaan.",
"Log out" => "Kirjaudu ulos",
"Automatic logon rejected!" => "Automaattinen sisäänkirjautuminen hylättiin!",
"If you did not change your password recently, your account may be compromised!" => "Jos et vaihtanut salasanaasi äskettäin, tilisi saattaa olla murrettu.",

View File

@ -44,11 +44,11 @@
"months ago" => "il y a plusieurs mois",
"last year" => "l'année dernière",
"years ago" => "il y a plusieurs années",
"Ok" => "Ok",
"Cancel" => "Annuler",
"Choose" => "Choisir",
"Cancel" => "Annuler",
"Yes" => "Oui",
"No" => "Non",
"Ok" => "Ok",
"The object type is not specified." => "Le type d'objet n'est pas spécifié.",
"Error" => "Erreur",
"The app name is not specified." => "Le nom de l'application n'est pas spécifié.",

View File

@ -44,11 +44,11 @@
"months ago" => "meses atrás",
"last year" => "último ano",
"years ago" => "anos atrás",
"Ok" => "Aceptar",
"Cancel" => "Cancelar",
"Choose" => "Escoller",
"Cancel" => "Cancelar",
"Yes" => "Si",
"No" => "Non",
"Ok" => "Aceptar",
"The object type is not specified." => "Non se especificou o tipo de obxecto.",
"Error" => "Erro",
"The app name is not specified." => "Non se especificou o nome do aplicativo.",

View File

@ -44,11 +44,11 @@
"months ago" => "חודשים",
"last year" => "שנה שעברה",
"years ago" => "שנים",
"Ok" => "בסדר",
"Cancel" => "ביטול",
"Choose" => "בחירה",
"Cancel" => "ביטול",
"Yes" => "כן",
"No" => "לא",
"Ok" => "בסדר",
"The object type is not specified." => "סוג הפריט לא צוין.",
"Error" => "שגיאה",
"The app name is not specified." => "שם היישום לא צוין.",

View File

@ -28,11 +28,11 @@
"months ago" => "mjeseci",
"last year" => "prošlu godinu",
"years ago" => "godina",
"Ok" => "U redu",
"Cancel" => "Odustani",
"Choose" => "Izaberi",
"Cancel" => "Odustani",
"Yes" => "Da",
"No" => "Ne",
"Ok" => "U redu",
"Error" => "Greška",
"Share" => "Podijeli",
"Error while sharing" => "Greška prilikom djeljenja",

View File

@ -44,11 +44,11 @@
"months ago" => "több hónapja",
"last year" => "tavaly",
"years ago" => "több éve",
"Ok" => "Ok",
"Cancel" => "Mégsem",
"Choose" => "Válasszon",
"Cancel" => "Mégsem",
"Yes" => "Igen",
"No" => "Nem",
"Ok" => "Ok",
"The object type is not specified." => "Az objektum típusa nincs megadva.",
"Error" => "Hiba",
"The app name is not specified." => "Az alkalmazás neve nincs megadva.",

View File

@ -44,11 +44,11 @@
"months ago" => "beberapa bulan lalu",
"last year" => "tahun kemarin",
"years ago" => "beberapa tahun lalu",
"Ok" => "Oke",
"Cancel" => "Batal",
"Choose" => "Pilih",
"Cancel" => "Batal",
"Yes" => "Ya",
"No" => "Tidak",
"Ok" => "Oke",
"The object type is not specified." => "Tipe objek tidak ditentukan.",
"Error" => "Galat",
"The app name is not specified." => "Nama aplikasi tidak ditentukan.",

View File

@ -43,11 +43,11 @@
"months ago" => "mánuðir síðan",
"last year" => "síðasta ári",
"years ago" => "einhverjum árum",
"Ok" => "Í lagi",
"Cancel" => "Hætta við",
"Choose" => "Veldu",
"Cancel" => "Hætta við",
"Yes" => "",
"No" => "Nei",
"Ok" => "Í lagi",
"The object type is not specified." => "Tegund ekki tilgreind",
"Error" => "Villa",
"The app name is not specified." => "Nafn forrits ekki tilgreint",

View File

@ -44,11 +44,12 @@
"months ago" => "mesi fa",
"last year" => "anno scorso",
"years ago" => "anni fa",
"Ok" => "Ok",
"Cancel" => "Annulla",
"Choose" => "Scegli",
"Cancel" => "Annulla",
"Error loading file picker template" => "Errore durante il caricamento del modello del selezionatore di file",
"Yes" => "",
"No" => "No",
"Ok" => "Ok",
"The object type is not specified." => "Il tipo di oggetto non è specificato.",
"Error" => "Errore",
"The app name is not specified." => "Il nome dell'applicazione non è specificato.",

Some files were not shown because too many files have changed in this diff Show More