reformat code added and changed phpdoc

This commit is contained in:
Florin Peter 2013-05-20 01:24:36 +02:00
parent b1d0e8f40b
commit 3b850a2524
11 changed files with 3175 additions and 2935 deletions

View File

@ -27,59 +27,57 @@ namespace OCA\Encryption;
require_once 'Crypt_Blowfish/Blowfish.php'; 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
/** /**
* Class for common cryptography functionality * Class for common cryptography functionality
*/ */
class Crypt { class Crypt
{
/** /**
* @brief return encryption mode client or server side encryption * @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' * @return string 'client' or 'server'
*/ */
public static function mode( $user = null ) { public static function mode($user = null)
{
return 'server'; return 'server';
} }
/** /**
* @brief Create a new encryption keypair * @brief Create a new encryption keypair
* @return array publicKey, privatekey * @return array publicKey, privatekey
*/ */
public static function createKeypair() { public static function createKeypair()
{
$res = openssl_pkey_new(array('private_key_bits' => 4096)); $res = openssl_pkey_new(array('private_key_bits' => 4096));
// Get private key // Get private key
openssl_pkey_export( $res, $privateKey ); openssl_pkey_export($res, $privateKey);
// Get public key // Get public key
$publicKey = openssl_pkey_get_details( $res ); $publicKey = openssl_pkey_get_details($res);
$publicKey = $publicKey['key']; $publicKey = $publicKey['key'];
return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) ); return (array('publicKey' => $publicKey, 'privateKey' => $privateKey));
} }
/** /**
* @brief Add arbitrary padding to encrypted data * @brief Add arbitrary padding to encrypted data
* @param string $data data to be padded * @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 * @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 * add two letters. It is impossible to achieve exactly 8192 length
* blocks with encryption alone, hence padding is added to achieve the * blocks with encryption alone, hence padding is added to achieve the
* required length. * required length.
*/ */
public static function addPadding( $data ) { public static function addPadding($data)
{
$padded = $data . 'xx'; $padded = $data . 'xx';
@ -87,16 +85,17 @@ class Crypt {
} }
/** /**
* @brief Remove arbitrary padding to encrypted data * @brief Remove arbitrary padding to encrypted data
* @param string $padded padded data to remove padding from * @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 ) { public static function removePadding($padded)
{
if ( substr( $padded, -2 ) == 'xx' ) { if (substr($padded, -2) == 'xx') {
$data = substr( $padded, 0, -2 ); $data = substr($padded, 0, -2);
return $data; return $data;
@ -109,31 +108,33 @@ class Crypt {
} }
/** /**
* @brief Check if a file's contents contains an IV and is symmetrically encrypted * @brief Check if a file's contents contains an IV and is symmetrically encrypted
* @return true / false * @param $content
* @note see also OCA\Encryption\Util->isEncryptedPath() * @return boolean
*/ * @note see also OCA\Encryption\Util->isEncryptedPath()
public static function isCatfileContent( $content ) { */
public static function isCatfileContent($content)
{
if ( !$content ) { if (!$content) {
return false; return false;
} }
$noPadding = self::removePadding( $content ); $noPadding = self::removePadding($content);
// Fetch encryption metadata from end of file // Fetch encryption metadata from end of file
$meta = substr( $noPadding, -22 ); $meta = substr($noPadding, -22);
// Fetch IV from end of file // Fetch IV from end of file
$iv = substr( $meta, -16 ); $iv = substr($meta, -16);
// Fetch identifier from start of metadata // Fetch identifier from start of metadata
$identifier = substr( $meta, 0, 6 ); $identifier = substr($meta, 0, 6);
if ( $identifier == '00iv00') { if ($identifier == '00iv00') {
return true; return true;
@ -150,36 +151,39 @@ class Crypt {
* @param string $path * @param string $path
* @return bool * @return bool
*/ */
public static function isEncryptedMeta( $path ) { public static function isEncryptedMeta($path)
{
// TODO: Use DI to get \OC\Files\Filesystem out of here // TODO: Use DI to get \OC\Files\Filesystem out of here
// Fetch all file metadata from DB // Fetch all file metadata from DB
$metadata = \OC\Files\Filesystem::getFileInfo( $path); $metadata = \OC\Files\Filesystem::getFileInfo($path);
// Return encryption status // Return encryption status
return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted']; return isset($metadata['encrypted']) and ( bool )$metadata['encrypted'];
} }
/** /**
* @brief Check if a file is encrypted via legacy system * @brief Check if a file is encrypted via legacy system
* @param string $relPath The path of the file, relative to user/data; * @param $data
* e.g. filename or /Docs/filename, NOT admin/files/filename * @param string $relPath The path of the file, relative to user/data;
* @return true / false * e.g. filename or /Docs/filename, NOT admin/files/filename
*/ * @return boolean
public static function isLegacyEncryptedContent( $data, $relPath ) { */
public static function isLegacyEncryptedContent($data, $relPath)
{
// Fetch all file metadata from DB // Fetch all file metadata from DB
$metadata = \OC\Files\Filesystem::getFileInfo( $relPath, '' ); $metadata = \OC\Files\Filesystem::getFileInfo($relPath, '');
// If a file is flagged with encryption in DB, but isn't a // If a file is flagged with encryption in DB, but isn't a
// valid content + IV combination, it's probably using the // valid content + IV combination, it's probably using the
// legacy encryption system // legacy encryption system
if ( if (
isset( $metadata['encrypted'] ) isset($metadata['encrypted'])
and $metadata['encrypted'] === true and $metadata['encrypted'] === true
and ! self::isCatfileContent( $data ) and !self::isCatfileContent($data)
) { ) {
return true; return true;
@ -192,19 +196,20 @@ class Crypt {
} }
/** /**
* @brief Symmetrically encrypt a string * @brief Symmetrically encrypt a string
* @returns encrypted file * @return string encrypted file content
*/ */
public static function encrypt( $plainContent, $iv, $passphrase = '' ) { public static function encrypt($plainContent, $iv, $passphrase = '')
{
if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { if ($encryptedContent = openssl_encrypt($plainContent, 'AES-128-CFB', $passphrase, false, $iv)) {
return $encryptedContent; return $encryptedContent;
} else { } else {
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR);
return false; return false;
@ -212,20 +217,21 @@ class Crypt {
} }
/** /**
* @brief Symmetrically decrypt a string * @brief Symmetrically decrypt a string
* @returns decrypted file * @return string decrypted file content
*/ */
public static function decrypt( $encryptedContent, $iv, $passphrase ) { public static function decrypt($encryptedContent, $iv, $passphrase)
{
if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { if ($plainContent = openssl_decrypt($encryptedContent, 'AES-128-CFB', $passphrase, false, $iv)) {
return $plainContent; return $plainContent;
} else { } else {
throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' ); throw new \Exception('Encryption library: Decryption (symmetric) of content failed');
return false; return false;
@ -233,13 +239,14 @@ class Crypt {
} }
/** /**
* @brief Concatenate encrypted data with its IV and padding * @brief Concatenate encrypted data with its IV and padding
* @param string $content content to be concatenated * @param string $content content to be concatenated
* @param string $iv IV to be concatenated * @param string $iv IV to be concatenated
* @returns string concatenated content * @returns string concatenated content
*/ */
public static function concatIv ( $content, $iv ) { public static function concatIv($content, $iv)
{
$combined = $content . '00iv00' . $iv; $combined = $content . '00iv00' . $iv;
@ -247,41 +254,45 @@ class Crypt {
} }
/** /**
* @brief Split concatenated data and IV into respective parts * @brief Split concatenated data and IV into respective parts
* @param string $catFile concatenated data to be split * @param string $catFile concatenated data to be split
* @returns array keys: encrypted, iv * @returns array keys: encrypted, iv
*/ */
public static function splitIv ( $catFile ) { public static function splitIv($catFile)
{
// Fetch encryption metadata from end of file // Fetch encryption metadata from end of file
$meta = substr( $catFile, -22 ); $meta = substr($catFile, -22);
// Fetch IV from end of file // Fetch IV from end of file
$iv = substr( $meta, -16 ); $iv = substr($meta, -16);
// Remove IV and IV identifier text to expose encrypted content // Remove IV and IV identifier text to expose encrypted content
$encrypted = substr( $catFile, 0, -22 ); $encrypted = substr($catFile, 0, -22);
$split = array( $split = array(
'encrypted' => $encrypted 'encrypted' => $encrypted
, 'iv' => $iv , 'iv' => $iv
); );
return $split; return $split;
} }
/** /**
* @brief Symmetrically encrypts a string and returns keyfile content * @brief Symmetrically encrypts a string and returns keyfile content
* @param $plainContent content to be encrypted in keyfile * @param string $plainContent content to be encrypted in keyfile
* @returns encrypted content combined with IV * @param string $passphrase
* @note IV need not be specified, as it will be stored in the returned keyfile * @return bool|string
* and remain accessible therein. * @return string encrypted content combined with IV
*/ * @note IV need not be specified, as it will be stored in the returned keyfile
public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) { * and remain accessible therein.
*/
public static function symmetricEncryptFileContent($plainContent, $passphrase = '')
{
if ( !$plainContent ) { if (!$plainContent) {
return false; return false;
@ -289,18 +300,18 @@ class Crypt {
$iv = self::generateIv(); $iv = self::generateIv();
if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { if ($encryptedContent = self::encrypt($plainContent, $iv, $passphrase)) {
// Combine content to encrypt with IV identifier and actual IV // Combine content to encrypt with IV identifier and actual IV
$catfile = self::concatIv( $encryptedContent, $iv ); $catfile = self::concatIv($encryptedContent, $iv);
$padded = self::addPadding( $catfile ); $padded = self::addPadding($catfile);
return $padded; return $padded;
} else { } else {
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR);
return false; return false;
@ -310,29 +321,34 @@ class Crypt {
/** /**
* @brief Symmetrically decrypts keyfile content * @brief Symmetrically decrypts keyfile content
* @param string $source * @param $keyfileContent
* @param string $target * @param string $passphrase
* @param string $key the decryption key * @throws \Exception
* @returns decrypted content * @return bool|string
* * @internal param string $source
* This function decrypts a file * @internal param string $target
*/ * @internal param string $key the decryption key
public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) { * @returns string decrypted content
*
* This function decrypts a file
*/
public static function symmetricDecryptFileContent($keyfileContent, $passphrase = '')
{
if ( !$keyfileContent ) { if (!$keyfileContent) {
throw new \Exception( 'Encryption library: no data provided for decryption' ); throw new \Exception('Encryption library: no data provided for decryption');
} }
// Remove padding // Remove padding
$noPadding = self::removePadding( $keyfileContent ); $noPadding = self::removePadding($keyfileContent);
// Split into enc data and catfile // Split into enc data and catfile
$catfile = self::splitIv( $noPadding ); $catfile = self::splitIv($noPadding);
if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { if ($plainContent = self::decrypt($catfile['encrypted'], $catfile['iv'], $passphrase)) {
return $plainContent; return $plainContent;
@ -341,22 +357,23 @@ class Crypt {
} }
/** /**
* @brief Creates symmetric keyfile content using a generated key * @brief Creates symmetric keyfile content using a generated key
* @param string $plainContent content to be encrypted * @param string $plainContent content to be encrypted
* @returns array keys: key, encrypted * @returns array keys: key, encrypted
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method * @note symmetricDecryptFileContent() can be used to decrypt files created using this method
* *
* This function decrypts a file * This function decrypts a file
*/ */
public static function symmetricEncryptFileContentKeyfile( $plainContent ) { public static function symmetricEncryptFileContentKeyfile($plainContent)
{
$key = self::generateKey(); $key = self::generateKey();
if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) { if ($encryptedContent = self::symmetricEncryptFileContent($plainContent, $key)) {
return array( return array(
'key' => $key 'key' => $key
, 'encrypted' => $encryptedContent , 'encrypted' => $encryptedContent
); );
} else { } else {
@ -368,20 +385,21 @@ class Crypt {
} }
/** /**
* @brief Create asymmetrically encrypted keyfile content using a generated key * @brief Create asymmetrically encrypted keyfile content using a generated key
* @param string $plainContent content to be encrypted * @param string $plainContent content to be encrypted
* @param array $publicKeys array keys must be the userId of corresponding user * @param array $publicKeys array keys must be the userId of corresponding user
* @returns array keys: keys (array, key = userId), data * @returns array keys: keys (array, key = userId), data
* @note symmetricDecryptFileContent() can decrypt files created using this method * @note symmetricDecryptFileContent() can decrypt files created using this method
*/ */
public static function multiKeyEncrypt( $plainContent, array $publicKeys ) { public static function multiKeyEncrypt($plainContent, array $publicKeys)
{
// openssl_seal returns false without errors if $plainContent // openssl_seal returns false without errors if $plainContent
// is empty, so trigger our own error // is empty, so trigger our own error
if ( empty( $plainContent ) ) { if (empty($plainContent)) {
trigger_error( "Cannot mutliKeyEncrypt empty plain content" ); trigger_error("Cannot mutliKeyEncrypt empty plain content");
throw new \Exception( 'Cannot mutliKeyEncrypt empty plain content' ); throw new \Exception('Cannot mutliKeyEncrypt empty plain content');
} }
@ -389,13 +407,13 @@ class Crypt {
$sealed = ''; $sealed = '';
$shareKeys = array(); $shareKeys = array();
if( openssl_seal( $plainContent, $sealed, $shareKeys, $publicKeys ) ) { if (openssl_seal($plainContent, $sealed, $shareKeys, $publicKeys)) {
$i = 0; $i = 0;
// Ensure each shareKey is labelled with its // Ensure each shareKey is labelled with its
// corresponding userId // corresponding userId
foreach ( $publicKeys as $userId => $publicKey ) { foreach ($publicKeys as $userId => $publicKey) {
$mappedShareKeys[$userId] = $shareKeys[$i]; $mappedShareKeys[$userId] = $shareKeys[$i];
$i++; $i++;
@ -404,7 +422,7 @@ class Crypt {
return array( return array(
'keys' => $mappedShareKeys 'keys' => $mappedShareKeys
, 'data' => $sealed , 'data' => $sealed
); );
} else { } else {
@ -416,28 +434,33 @@ class Crypt {
} }
/** /**
* @brief Asymmetrically encrypt a file using multiple public keys * @brief Asymmetrically encrypt a file using multiple public keys
* @param string $plainContent content to be encrypted * @param $encryptedContent
* @returns string $plainContent decrypted string * @param $shareKey
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method * @param $privateKey
* * @return bool
* This function decrypts a file * @internal param string $plainContent content to be encrypted
*/ * @returns string $plainContent decrypted string
public static function multiKeyDecrypt( $encryptedContent, $shareKey, $privateKey ) { * @note symmetricDecryptFileContent() can be used to decrypt files created using this method
*
* This function decrypts a file
*/
public static function multiKeyDecrypt($encryptedContent, $shareKey, $privateKey)
{
if ( !$encryptedContent ) { if (!$encryptedContent) {
return false; return false;
} }
if ( openssl_open( $encryptedContent, $plainContent, $shareKey, $privateKey ) ) { if (openssl_open($encryptedContent, $plainContent, $shareKey, $privateKey)) {
return $plainContent; return $plainContent;
} else { } else {
\OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR);
return false; return false;
@ -445,27 +468,29 @@ class Crypt {
} }
/** /**
* @brief Asymetrically encrypt a string using a public key * @brief Asymetrically encrypt a string using a public key
* @returns encrypted file * @return string encrypted file
*/ */
public static function keyEncrypt( $plainContent, $publicKey ) { public static function keyEncrypt($plainContent, $publicKey)
{
openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey ); openssl_public_encrypt($plainContent, $encryptedContent, $publicKey);
return $encryptedContent; return $encryptedContent;
} }
/** /**
* @brief Asymetrically decrypt a file using a private key * @brief Asymetrically decrypt a file using a private key
* @returns decrypted file * @return string decrypted file
*/ */
public static function keyDecrypt( $encryptedContent, $privatekey ) { public static function keyDecrypt($encryptedContent, $privatekey)
{
$result = @openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey ); $result = @openssl_private_decrypt($encryptedContent, $plainContent, $privatekey);
if ( $result ) { if ($result) {
return $plainContent; return $plainContent;
} }
@ -477,26 +502,27 @@ class Crypt {
* @brief Generates a pseudo random initialisation vector * @brief Generates a pseudo random initialisation vector
* @return String $iv generated IV * @return String $iv generated IV
*/ */
public static function generateIv() { public static function generateIv()
{
if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) { if ($random = openssl_random_pseudo_bytes(12, $strong)) {
if ( !$strong ) { if (!$strong) {
// If OpenSSL indicates randomness is insecure, log error // If OpenSSL indicates randomness is insecure, log error
\OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN ); \OC_Log::write('Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN);
} }
// We encode the iv purely for string manipulation // We encode the iv purely for string manipulation
// purposes - it gets decoded before use // purposes - it gets decoded before use
$iv = base64_encode( $random ); $iv = base64_encode($random);
return $iv; return $iv;
} else { } else {
throw new Exception( 'Generating IV failed' ); throw new \Exception('Generating IV failed');
} }
@ -506,15 +532,16 @@ class Crypt {
* @brief Generate a pseudo random 1024kb ASCII key * @brief Generate a pseudo random 1024kb ASCII key
* @returns $key Generated key * @returns $key Generated key
*/ */
public static function generateKey() { public static function generateKey()
{
// Generate key // Generate key
if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { if ($key = base64_encode(openssl_random_pseudo_bytes(183, $strong))) {
if ( !$strong ) { if (!$strong) {
// If OpenSSL indicates randomness is insecure, log error // 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()');
} }
@ -535,11 +562,12 @@ class Crypt {
* *
* if the key is left out, the default handeler will be used * if the key is left out, the default handeler will be used
*/ */
public static function getBlowfish( $key = '' ) { public static function getBlowfish($key = '')
{
if ( $key ) { if ($key) {
return new \Crypt_Blowfish( $key ); return new \Crypt_Blowfish($key);
} else { } else {
@ -549,13 +577,18 @@ class Crypt {
} }
public static function legacyCreateKey( $passphrase ) { /**
* @param $passphrase
* @return mixed
*/
public static function legacyCreateKey($passphrase)
{
// Generate a random integer // Generate a random integer
$key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); $key = mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999);
// Encrypt the key with the passphrase // Encrypt the key with the passphrase
$legacyEncKey = self::legacyEncrypt( $key, $passphrase ); $legacyEncKey = self::legacyEncrypt($key, $passphrase);
return $legacyEncKey; return $legacyEncKey;
@ -563,42 +596,55 @@ class Crypt {
/** /**
* @brief encrypts content using legacy blowfish system * @brief encrypts content using legacy blowfish system
* @param $content the cleartext message you want to encrypt * @param string $content the cleartext message you want to encrypt
* @param $key the encryption key (optional) * @param string $passphrase
* @returns encrypted content * @return
* @internal param \OCA\Encryption\the $key encryption key (optional)
* @returns string encrypted content
* *
* This function encrypts an content * This function encrypts an content
*/ */
public static function legacyEncrypt( $content, $passphrase = '' ) { public static function legacyEncrypt($content, $passphrase = '')
{
$bf = self::getBlowfish( $passphrase ); $bf = self::getBlowfish($passphrase);
return $bf->encrypt( $content ); return $bf->encrypt($content);
} }
/** /**
* @brief decrypts content using legacy blowfish system * @brief decrypts content using legacy blowfish system
* @param $content the cleartext message you want to decrypt * @param string $content the cleartext message you want to decrypt
* @param $key the encryption key (optional) * @param string $passphrase
* @returns cleartext content * @return string
* * @internal param \OCA\Encryption\the $key encryption key (optional)
* This function decrypts an content * @return string cleartext content
*/ *
public static function legacyDecrypt( $content, $passphrase = '' ) { * This function decrypts an content
*/
public static function legacyDecrypt($content, $passphrase = '')
{
$bf = self::getBlowfish( $passphrase ); $bf = self::getBlowfish($passphrase);
$decrypted = $bf->decrypt( $content ); $decrypted = $bf->decrypt($content);
return rtrim($decrypted, "\0");; return rtrim($decrypted, "\0");;
} }
private static function legacyBlockDecrypt($data, $key='',$maxLength=0) { /**
* @param $data
* @param string $key
* @param int $maxLength
* @return string
*/
private static function legacyBlockDecrypt($data, $key = '', $maxLength = 0)
{
$result = ''; $result = '';
while (strlen($data)) { while (strlen($data)) {
$result.=self::legacyDecrypt(substr($data, 0, 8192), $key); $result .= self::legacyDecrypt(substr($data, 0, 8192), $key);
$data = substr($data, 8192); $data = substr($data, 8192);
} }
if ($maxLength > 0) { if ($maxLength > 0) {
@ -608,17 +654,26 @@ class Crypt {
} }
} }
public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKeys, $newPassphrase, $path ) { /**
* @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 ); $decrypted = self::legacyBlockDecrypt($legacyEncryptedContent, $legacyPassphrase);
// Encrypt plain data, generate keyfile & encrypted file // Encrypt plain data, generate keyfile & encrypted file
$cryptedData = self::symmetricEncryptFileContentKeyfile( $decrypted ); $cryptedData = self::symmetricEncryptFileContentKeyfile($decrypted);
// Encrypt plain keyfile to multiple sharefiles // Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $cryptedData['key'], $publicKeys ); $multiEncrypted = Crypt::multiKeyEncrypt($cryptedData['key'], $publicKeys);
return array( 'data' => $cryptedData['encrypted'], 'filekey' => $multiEncrypted['data'], 'sharekeys' => $multiEncrypted['keys'] ); return array('data' => $cryptedData['encrypted'], 'filekey' => $multiEncrypted['data'], 'sharekeys' => $multiEncrypted['keys']);
} }

View File

@ -23,76 +23,82 @@
namespace OCA\Encryption; namespace OCA\Encryption;
/** /**
* @brief Class to manage registration of hooks an various helper methods * @brief Class to manage registration of hooks an various helper methods
*/ */
/** /**
* Class Helper * Class Helper
* @package OCA\Encryption * @package OCA\Encryption
*/ */
class Helper { class Helper
{
/** /**
* @brief register share related hooks * @brief register share related hooks
* *
*/ */
public static function registerShareHooks() { public static function registerShareHooks()
{
\OCP\Util::connectHook( 'OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared' ); \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_shared', 'OCA\Encryption\Hooks', 'postShared');
\OCP\Util::connectHook( 'OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare' ); \OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare');
} }
/** /**
* @brief register user related hooks * @brief register user related hooks
* *
*/ */
public static function registerUserHooks() { public static function registerUserHooks()
{
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); \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_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase');
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' ); \OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser');
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' ); \OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser');
} }
/** /**
* @brief register webdav related hooks * @brief register webdav related hooks
* *
*/ */
public static function registerWebdavHooks() { public static function registerWebdavHooks()
{
} }
/** /**
* @brief register filesystem related hooks * @brief register filesystem related hooks
* *
*/ */
public static function registerFilesystemHooks() { public static function registerFilesystemHooks()
{
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename'); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
} }
/** /**
* @brief setup user for files_encryption * @brief setup user for files_encryption
* *
* @param Util $util * @param Util $util
* @param string $password * @param string $password
* @return bool * @return bool
*/ */
public static function setupUser($util, $password) { public static function setupUser($util, $password)
// Check files_encryption infrastructure is ready for action {
if ( ! $util->ready() ) { // 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 ); \OC_Log::write('Encryption library', 'User account "' . $util->getUserId() . '" is not ready for encryption; configuration started', \OC_Log::DEBUG);
if(!$util->setupServerSide( $password )) { if (!$util->setupServerSide($password)) {
return false; return false;
} }
} }
return true; return true;
} }
/** /**
* @brief enable recovery * @brief enable recovery
@ -103,7 +109,8 @@ class Helper {
* @internal param string $password * @internal param string $password
* @return bool * @return bool
*/ */
public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) { public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword)
{
$view = new \OC\Files\View('/'); $view = new \OC\Files\View('/');
if ($recoveryKeyId === null) { if ($recoveryKeyId === null) {
@ -139,7 +146,7 @@ class Helper {
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey); $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. // create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']); $encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']);
if (!$view->is_dir('/control-file')) { if (!$view->is_dir('/control-file')) {
$view->mkdir('/control-file'); $view->mkdir('/control-file');
} }
@ -170,7 +177,8 @@ class Helper {
* @param $recoveryPassword * @param $recoveryPassword
* @return bool * @return bool
*/ */
public static function adminDisableRecovery($recoveryPassword) { public static function adminDisableRecovery($recoveryPassword)
{
$util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); $util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$return = $util->checkRecoveryPassword($recoveryPassword); $return = $util->checkRecoveryPassword($recoveryPassword);

View File

@ -27,7 +27,8 @@ namespace OCA\Encryption;
* @brief Class to manage storage and retrieval of encryption keys * @brief Class to manage storage and retrieval of encryption keys
* @note Where a method requires a view object, it's root must be '/' * @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 * @brief retrieve the ENCRYPTED private key from a user
@ -37,16 +38,17 @@ class Keymanager {
* @return string private key or false (hopefully) * @return string private key or false (hopefully)
* @note the key returned by this method must be decrypted before use * @note the key returned by this method must be decrypted before use
*/ */
public static function getPrivateKey( \OC_FilesystemView $view, $user ) { 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; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$key = $view->file_get_contents( $path ); $key = $view->file_get_contents($path);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
return $key; return $key;
} }
@ -57,16 +59,17 @@ class Keymanager {
* @param $userId * @param $userId
* @return string public key or false * @return string public key or false
*/ */
public static function getPublicKey( \OC_FilesystemView $view, $userId ) { public static function getPublicKey(\OC_FilesystemView $view, $userId)
{
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$result = $view->file_get_contents( '/public-keys/' . $userId . '.public.key' ); $result = $view->file_get_contents('/public-keys/' . $userId . '.public.key');
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
return $result; return $result;
} }
@ -76,11 +79,12 @@ class Keymanager {
* @param $userId * @param $userId
* @return array keys: privateKey, publicKey * @return array keys: privateKey, publicKey
*/ */
public static function getUserKeys( \OC_FilesystemView $view, $userId ) { public static function getUserKeys(\OC_FilesystemView $view, $userId)
{
return array( return array(
'publicKey' => self::getPublicKey( $view, $userId ) 'publicKey' => self::getPublicKey($view, $userId)
, 'privateKey' => self::getPrivateKey( $view, $userId ) , 'privateKey' => self::getPrivateKey($view, $userId)
); );
} }
@ -91,13 +95,14 @@ class Keymanager {
* @param array $userIds * @param array $userIds
* @return array of public keys for the specified users * @return array of public keys for the specified users
*/ */
public static function getPublicKeys( \OC_FilesystemView $view, array $userIds ) { public static function getPublicKeys(\OC_FilesystemView $view, array $userIds)
{
$keys = array(); $keys = array();
foreach ( $userIds as $userId ) { foreach ($userIds as $userId) {
$keys[$userId] = self::getPublicKey( $view, $userId ); $keys[$userId] = self::getPublicKey($view, $userId);
} }
@ -108,46 +113,50 @@ class Keymanager {
/** /**
* @brief store file encryption key * @brief store file encryption key
* *
* @param \OC_FilesystemView $view
* @param string $path relative path of the file, including filename * @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 * @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 * asymmetrically encrypt the keyfile before passing it to this method
*/ */
public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) { public static function setFileKey(\OC_FilesystemView $view, $path, $userId, $catfile)
{
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $path ); list($owner, $filename) = $util->getUidAndFilename($path);
$basePath = '/' . $owner . '/files_encryption/keyfiles'; $basePath = '/' . $owner . '/files_encryption/keyfiles';
$targetPath = self::keySetPreparation( $view, $filename, $basePath, $owner ); $targetPath = self::keySetPreparation($view, $filename, $basePath, $owner);
if ( !$view->is_dir( $basePath . '/' . $targetPath ) ) { if (!$view->is_dir($basePath . '/' . $targetPath)) {
// create all parent folders // create all parent folders
$info = pathinfo( $basePath . '/' . $targetPath ); $info = pathinfo($basePath . '/' . $targetPath);
$keyfileFolderName = $view->getLocalFolder( $info['dirname'] ); $keyfileFolderName = $view->getLocalFolder($info['dirname']);
if ( ! file_exists( $keyfileFolderName ) ) { if (!file_exists($keyfileFolderName)) {
mkdir( $keyfileFolderName, 0750, true ); mkdir($keyfileFolderName, 0750, true);
} }
} }
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $targetPath ) ) { if (self::isPartialFilePath($targetPath)) {
$result = $view->file_put_contents( $basePath . '/' . self::fixPartialFilePath( $targetPath ) . '.key', $catfile ); $result = $view->file_put_contents($basePath . '/' . self::fixPartialFilePath($targetPath) . '.key', $catfile);
} else { } else {
$result = $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); $result = $view->file_put_contents($basePath . '/' . $targetPath . '.key', $catfile);
} }
@ -163,7 +172,8 @@ class Keymanager {
* @return string File path without .part extension * @return string File path without .part extension
* @note this is needed for reusing keys * @note this is needed for reusing keys
*/ */
public static function fixPartialFilePath( $path ) { public static function fixPartialFilePath($path)
{
if (preg_match('/\.part$/', $path)) { if (preg_match('/\.part$/', $path)) {
@ -185,9 +195,10 @@ class Keymanager {
* @param string $path Path that may identify a .part file * @param string $path Path that may identify a .part file
* @return bool * @return bool
*/ */
public static function isPartialFilePath( $path ) { public static function isPartialFilePath($path)
{
if ( preg_match('/\.part$/', $path ) ) { if (preg_match('/\.part$/', $path)) {
return true; return true;
@ -198,6 +209,7 @@ class Keymanager {
} }
} }
/** /**
* @brief retrieve keyfile for an encrypted file * @brief retrieve keyfile for an encrypted file
* @param \OC_FilesystemView $view * @param \OC_FilesystemView $view
@ -208,14 +220,15 @@ class Keymanager {
* @note The keyfile returned is asymmetrically encrypted. Decryption * @note The keyfile returned is asymmetrically encrypted. Decryption
* of the keyfile must be performed by client code * of the keyfile must be performed by client code
*/ */
public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getFileKey(\OC_FilesystemView $view, $userId, $filePath)
{
// try reusing key file if part file // try reusing key file if part file
if ( self::isPartialFilePath( $filePath ) ) { if (self::isPartialFilePath($filePath)) {
$result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) ); $result = self::getFileKey($view, $userId, self::fixPartialFilePath($filePath));
if ( $result ) { if ($result) {
return $result; return $result;
@ -225,20 +238,20 @@ class Keymanager {
$util = new Util($view, \OCP\User::getUser()); $util = new Util($view, \OCP\User::getUser());
list($owner, $filename) = $util->getUidAndFilename($filePath); list($owner, $filename) = $util->getUidAndFilename($filePath);
$filePath_f = ltrim( $filename, '/' ); $filePath_f = ltrim($filename, '/');
$keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key'; $keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key';
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( $view->file_exists( $keyfilePath ) ) { if ($view->file_exists($keyfilePath)) {
$result = $view->file_get_contents( $keyfilePath ); $result = $view->file_get_contents($keyfilePath);
} else { } else {
$result = false; $result = false;
} }
@ -251,33 +264,34 @@ class Keymanager {
/** /**
* @brief Delete a keyfile * @brief Delete a keyfile
* *
* @param OC_FilesystemView $view * @param \OC_FilesystemView $view
* @param string $userId username * @param string $userId username
* @param string $path path of the file the key belongs to * @param string $path path of the file the key belongs to
* @return bool Outcome of unlink operation * @return bool Outcome of unlink operation
* @note $path must be relative to data/user/files. e.g. mydoc.txt NOT * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT
* /data/admin/files/mydoc.txt * /data/admin/files/mydoc.txt
*/ */
public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) { public static function deleteFileKey(\OC_FilesystemView $view, $userId, $path)
{
$trimmed = ltrim( $path, '/' ); $trimmed = ltrim($path, '/');
$keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed; $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed;
$result = false; $result = false;
if ( $view->is_dir($keyPath) ) { if ($view->is_dir($keyPath)) {
$result = $view->unlink($keyPath); $result = $view->unlink($keyPath);
} else if ( $view->file_exists( $keyPath.'.key' ) ) { } else if ($view->file_exists($keyPath . '.key')) {
$result = $view->unlink( $keyPath.'.key' ); $result = $view->unlink($keyPath . '.key');
} }
if ( !$result ) { if (!$result) {
\OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR);
} }
@ -287,23 +301,24 @@ class Keymanager {
/** /**
* @brief store private key from the user * @brief store private key from the user
* @param string key * @param string $key
* @return bool * @return bool
* @note Encryption of the private key must be performed by client code * @note Encryption of the private key must be performed by client code
* as no encryption takes place here * as no encryption takes place here
*/ */
public static function setPrivateKey( $key ) { public static function setPrivateKey($key)
{
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); $view = new \OC_FilesystemView('/' . $user . '/files_encryption');
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); if (!$view->file_exists('')) $view->mkdir('');
$result = $view->file_put_contents( $user . '.private.key', $key ); $result = $view->file_put_contents($user . '.private.key', $key);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -314,32 +329,34 @@ class Keymanager {
/** /**
* @brief store private keys from the user * @brief store private keys from the user
* *
* @param string privatekey * @param string $privatekey
* @param string publickey * @param string $publickey
* @return bool true/false * @return bool true/false
*/ */
public static function setUserKeys($privatekey, $publickey) { public static function setUserKeys($privatekey, $publickey)
{
return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) ); return (self::setPrivateKey($privatekey) && self::setPublicKey($publickey));
} }
/** /**
* @brief store public key of the user * @brief store public key of the user
* *
* @param string key * @param string $key
* @return bool true/false * @return bool true/false
*/ */
public static function setPublicKey( $key ) { public static function setPublicKey($key)
{
$view = new \OC_FilesystemView( '/public-keys' ); $view = new \OC_FilesystemView('/public-keys');
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); if (!$view->file_exists('')) $view->mkdir('');
$result = $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); $result = $view->file_put_contents(\OCP\User::getUser() . '.public.key', $key);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -350,27 +367,30 @@ class Keymanager {
/** /**
* @brief store share key * @brief store share key
* *
* @param \OC_FilesystemView $view
* @param string $path relative path of the file, including filename * @param string $path relative path of the file, including filename
* @param string $key * @param $userId
* @param null $view * @param $shareKey
* @param string $dbClassName * @internal param string $key
* @internal param string $dbClassName
* @return bool true/false * @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 * asymmetrically encrypt the keyfile before passing it to this method
*/ */
public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { public static function setShareKey(\OC_FilesystemView $view, $path, $userId, $shareKey)
{
// Here we need the currently logged in user, while userId can be a different user // Here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list( $owner, $filename ) = $util->getUidAndFilename( $path ); list($owner, $filename) = $util->getUidAndFilename($path);
$basePath = '/' . $owner . '/files_encryption/share-keys'; $basePath = '/' . $owner . '/files_encryption/share-keys';
$shareKeyPath = self::keySetPreparation( $view, $filename, $basePath, $owner ); $shareKeyPath = self::keySetPreparation($view, $filename, $basePath, $owner);
// try reusing key file if part file // try reusing key file if part file
if(self::isPartialFilePath($shareKeyPath)) { if (self::isPartialFilePath($shareKeyPath)) {
$writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey'; $writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey';
@ -383,12 +403,12 @@ class Keymanager {
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$result = $view->file_put_contents( $writePath, $shareKey ); $result = $view->file_put_contents($writePath, $shareKey);
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( if (
is_int( $result ) is_int($result)
&& $result > 0 && $result > 0
) { ) {
@ -404,18 +424,22 @@ class Keymanager {
/** /**
* @brief store multiple share keys for a single file * @brief store multiple share keys for a single file
* @param \OC_FilesystemView $view
* @param $path
* @param array $shareKeys
* @return bool * @return bool
*/ */
public static function setShareKeys( \OC_FilesystemView $view, $path, array $shareKeys ) { public static function setShareKeys(\OC_FilesystemView $view, $path, array $shareKeys)
{
// $shareKeys must be an array with the following format: // $shareKeys must be an array with the following format:
// [userId] => [encrypted key] // [userId] => [encrypted key]
$result = true; $result = true;
foreach ( $shareKeys as $userId => $shareKey ) { foreach ($shareKeys as $userId => $shareKey) {
if ( ! self::setShareKey( $view, $path, $userId, $shareKey ) ) { if (!self::setShareKey($view, $path, $userId, $shareKey)) {
// If any of the keys are not set, flag false // If any of the keys are not set, flag false
$result = false; $result = false;
@ -439,14 +463,15 @@ class Keymanager {
* @note The sharekey returned is encrypted. Decryption * @note The sharekey returned is encrypted. Decryption
* of the keyfile must be performed by client code * of the keyfile must be performed by client code
*/ */
public static function getShareKey( \OC_FilesystemView $view, $userId, $filePath ) { public static function getShareKey(\OC_FilesystemView $view, $userId, $filePath)
{
// try reusing key file if part file // try reusing key file if part file
if(self::isPartialFilePath($filePath)) { if (self::isPartialFilePath($filePath)) {
$result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath)); $result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath));
if($result) { if ($result) {
return $result; return $result;
@ -458,14 +483,14 @@ class Keymanager {
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list($owner, $filename) = $util->getUidAndFilename($filePath); list($owner, $filename) = $util->getUidAndFilename($filePath);
$shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey'); $shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey');
if ( $view->file_exists( $shareKeyPath ) ) { if ($view->file_exists($shareKeyPath)) {
$result = $view->file_get_contents( $shareKeyPath ); $result = $view->file_get_contents($shareKeyPath);
} else { } else {
@ -482,16 +507,17 @@ class Keymanager {
/** /**
* @brief delete all share keys of a given file * @brief delete all share keys of a given file
* @param \OC_FilesystemView $view * @param \OC_FilesystemView $view
* @param type $userId owner of the file * @param string $userId owner of the file
* @param type $filePath path to the file, relative to the owners file dir * @param string $filePath path to the file, relative to the owners file dir
*/ */
public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath) { public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath)
{
if ($view->is_dir($userId.'/files/'.$filePath)) { if ($view->is_dir($userId . '/files/' . $filePath)) {
$view->unlink($userId.'/files_encryption/share-keys/'.$filePath); $view->unlink($userId . '/files_encryption/share-keys/' . $filePath);
} else { } else {
$localKeyPath = $view->getLocalFile($userId.'/files_encryption/share-keys/'.$filePath); $localKeyPath = $view->getLocalFile($userId . '/files_encryption/share-keys/' . $filePath);
$matches = glob(preg_quote($localKeyPath).'*.shareKey'); $matches = glob(preg_quote($localKeyPath) . '*.shareKey');
foreach ($matches as $ma) { foreach ($matches as $ma) {
unlink($ma); unlink($ma);
} }
@ -501,13 +527,14 @@ class Keymanager {
/** /**
* @brief Delete a single user's shareKey for a single file * @brief Delete a single user's shareKey for a single file
*/ */
public static function delShareKey( \OC_FilesystemView $view, $userIds, $filePath ) { public static function delShareKey(\OC_FilesystemView $view, $userIds, $filePath)
{
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
//here we need the currently logged in user, while userId can be a different user //here we need the currently logged in user, while userId can be a different user
$util = new Util( $view, \OCP\User::getUser() ); $util = new Util($view, \OCP\User::getUser());
list($owner, $filename) = $util->getUidAndFilename($filePath); list($owner, $filename) = $util->getUidAndFilename($filePath);
@ -515,7 +542,7 @@ class Keymanager {
$result = false; $result = false;
if ( $view->is_dir($shareKeyPath) ) { if ($view->is_dir($shareKeyPath)) {
$localPath = \OC_Filesystem::normalizePath($view->getLocalFolder($shareKeyPath)); $localPath = \OC_Filesystem::normalizePath($view->getLocalFolder($shareKeyPath));
$result = self::recursiveDelShareKeys($localPath, $userIds); $result = self::recursiveDelShareKeys($localPath, $userIds);
@ -523,15 +550,15 @@ class Keymanager {
} else { } else {
foreach ($userIds as $userId) { foreach ($userIds as $userId) {
$view->unlink($shareKeyPath.'.'.$userId.'.shareKey'); $view->unlink($shareKeyPath . '.' . $userId . '.shareKey');
} }
$result = true; $result = true;
} }
if ( ! $result ) { if (!$result) {
\OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $shareKeyPath, \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Could not delete shareKey; does not exist: "' . $shareKeyPath, \OC_Log::ERROR);
} }
@ -544,19 +571,21 @@ class Keymanager {
/** /**
* @brief recursively delete share keys from given users * @brief recursively delete share keys from given users
* *
* @param type $dir directory * @param string $dir directory
* @param type $userIds user ids for which the share keys should be deleted * @param array $userIds user ids for which the share keys should be deleted
*/ */
private static function recursiveDelShareKeys($dir, $userIds) { private static function recursiveDelShareKeys($dir, $userIds)
{
foreach ($userIds as $userId) { foreach ($userIds as $userId) {
$completePath = $dir.'/.*'.'.'.$userId.'.shareKey'; $completePath = $dir . '/.*' . '.' . $userId . '.shareKey';
$matches = glob(preg_quote($dir).'/*'.preg_quote('.'.$userId.'.shareKey')); $matches = glob(preg_quote($dir) . '/*' . preg_quote('.' . $userId . '.shareKey'));
} }
foreach ($matches as $ma) { /** @var $matches array */
foreach ($matches as $ma) {
unlink($ma); unlink($ma);
} }
$subdirs = $directories = glob(preg_quote($dir) . '/*' , GLOB_ONLYDIR); $subdirs = $directories = glob(preg_quote($dir) . '/*', GLOB_ONLYDIR);
foreach ( $subdirs as $subdir ) { foreach ($subdirs as $subdir) {
self::recursiveDelShareKeys($subdir, $userIds); self::recursiveDelShareKeys($subdir, $userIds);
} }
return true; return true;
@ -565,16 +594,17 @@ class Keymanager {
/** /**
* @brief Make preparations to vars and filesystem for saving a keyfile * @brief Make preparations to vars and filesystem for saving a keyfile
*/ */
public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) { public static function keySetPreparation(\OC_FilesystemView $view, $path, $basePath, $userId)
{
$targetPath = ltrim( $path, '/' ); $targetPath = ltrim($path, '/');
$path_parts = pathinfo( $targetPath ); $path_parts = pathinfo($targetPath);
// If the file resides within a subdirectory, create it // If the file resides within a subdirectory, create it
if ( if (
isset( $path_parts['dirname'] ) isset($path_parts['dirname'])
&& ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] ) && !$view->file_exists($basePath . '/' . $path_parts['dirname'])
) { ) {
$sub_dirs = explode(DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname']); $sub_dirs = explode(DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname']);
$dir = ''; $dir = '';
@ -590,35 +620,20 @@ class Keymanager {
} }
/**
* @brief change password of private encryption key
*
* @param string $oldpasswd old password
* @param string $newpasswd new password
* @return bool true/false
*/
public static function changePasswd($oldpasswd, $newpasswd) {
if ( \OCP\User::checkPassword(\OCP\User::getUser(), $newpasswd) ) {
return Crypt::changekeypasscode($oldpasswd, $newpasswd);
}
return false;
}
/** /**
* @brief Fetch the legacy encryption key from user files * @brief Fetch the legacy encryption key from user files
* @param string $login used to locate the legacy key * @internal param string $login used to locate the legacy key
* @param string $passphrase used to decrypt the legacy key * @internal param string $passphrase used to decrypt the legacy key
* @return true / false * @return boolean
* *
* if the key is left out, the default handeler will be used * if the key is left out, the default handeler will be used
*/ */
public function getLegacyKey() { public function getLegacyKey()
{
$user = \OCP\User::getUser(); $user = \OCP\User::getUser();
$view = new \OC_FilesystemView( '/' . $user ); $view = new \OC_FilesystemView('/' . $user);
return $view->file_get_contents( 'encryption.key' ); return $view->file_get_contents('encryption.key');
} }

View File

@ -1,36 +1,41 @@
<?php <?php
/** /**
* ownCloud * ownCloud
* *
* @author Sam Tuke, Robin Appelman * @author Sam Tuke, Robin Appelman
* @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman * @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman
* icewind1991@gmail.com * icewind1991@gmail.com
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 3 of the License, or any later version. * version 3 of the License, or any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
* *
* You should have received a copy of the GNU Affero General Public * 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/>. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
/** /**
* @brief Encryption proxy which handles filesystem operations before and after * @brief Encryption proxy which handles filesystem operations before and after
* execution and encrypts, and handles keyfiles accordingly. Used for * execution and encrypts, and handles keyfiles accordingly. Used for
* webui. * webui.
*/ */
namespace OCA\Encryption; 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 $blackList = null; //mimetypes blacklisted from encryption
@ -43,12 +48,13 @@ class Proxy extends \OC_FileProxy {
* *
* Tests if server side encryption is enabled, and file is allowed by blacklists * Tests if server side encryption is enabled, and file is allowed by blacklists
*/ */
private static function shouldEncrypt( $path ) { private static function shouldEncrypt($path)
{
if ( is_null( self::$enableEncryption ) ) { if (is_null(self::$enableEncryption)) {
if ( if (
\OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true' \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') == 'true'
&& Crypt::mode() == 'server' && Crypt::mode() == 'server'
) { ) {
@ -62,27 +68,27 @@ class Proxy extends \OC_FileProxy {
} }
if ( !self::$enableEncryption ) { if (!self::$enableEncryption) {
return false; return false;
} }
if ( is_null(self::$blackList ) ) { if (is_null(self::$blackList)) {
self::$blackList = explode(',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', '' ) ); self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', ''));
} }
if ( Crypt::isCatfileContent( $path ) ) { if (Crypt::isCatfileContent($path)) {
return true; return true;
} }
$extension = substr( $path, strrpos( $path, '.' ) +1 ); $extension = substr($path, strrpos($path, '.') + 1);
if ( array_search( $extension, self::$blackList ) === false ) { if (array_search($extension, self::$blackList) === false) {
return true; return true;
@ -91,34 +97,40 @@ class Proxy extends \OC_FileProxy {
return false; return false;
} }
public function preFile_put_contents( $path, &$data ) { /**
* @param $path
* @param $data
* @return bool
*/
public function preFile_put_contents($path, &$data)
{
if ( self::shouldEncrypt( $path ) ) { if (self::shouldEncrypt($path)) {
// Stream put contents should have been converted to fopen // Stream put contents should have been converted to fopen
if ( !is_resource( $data ) ) { if (!is_resource($data)) {
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$session = new Session( $view ); $session = new Session($view);
$privateKey = $session->getPrivateKey(); $privateKey = $session->getPrivateKey();
$filePath = $util->stripUserFilesPath( $path ); $filePath = $util->stripUserFilesPath($path);
// Set the filesize for userland, before encrypting // Set the filesize for userland, before encrypting
$size = strlen( $data ); $size = strlen($data);
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Check if there is an existing key we can reuse // Check if there is an existing key we can reuse
if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) { if ($encKeyfile = Keymanager::getFileKey($view, $userId, $filePath)) {
// Fetch shareKey // Fetch shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $filePath ); $shareKey = Keymanager::getShareKey($view, $userId, $filePath);
// Decrypt the keyfile // Decrypt the keyfile
$plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); $plainKey = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
} else { } else {
@ -128,37 +140,37 @@ class Proxy extends \OC_FileProxy {
} }
// Encrypt data // Encrypt data
$encData = Crypt::symmetricEncryptFileContent( $data, $plainKey ); $encData = Crypt::symmetricEncryptFileContent($data, $plainKey);
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
// if file exists try to get sharing users // if file exists try to get sharing users
if($view->file_exists($path)) { if ($view->file_exists($path)) {
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId ); $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $filePath, $userId);
} else { } else {
$uniqueUserIds[] = $userId; $uniqueUserIds[] = $userId;
} }
// Fetch public keys for all users who will share the file // Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds ); $publicKeys = Keymanager::getPublicKeys($view, $uniqueUserIds);
// Encrypt plain keyfile to multiple sharefiles // Encrypt plain keyfile to multiple sharefiles
$multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); $multiEncrypted = Crypt::multiKeyEncrypt($plainKey, $publicKeys);
// Save sharekeys to user folders // Save sharekeys to user folders
Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] ); Keymanager::setShareKeys($view, $filePath, $multiEncrypted['keys']);
// Set encrypted keyfile as common varname // Set encrypted keyfile as common varname
$encKey = $multiEncrypted['data']; $encKey = $multiEncrypted['data'];
// Save keyfile for newly encrypted file in parallel directory tree // Save keyfile for newly encrypted file in parallel directory tree
Keymanager::setFileKey( $view, $filePath, $userId, $encKey ); Keymanager::setFileKey($view, $filePath, $userId, $encKey);
// Replace plain content with encrypted content by reference // Replace plain content with encrypted content by reference
$data = $encData; $data = $encData;
// Update the file cache with file info // Update the file cache with file info
\OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted'=>true, 'size' => strlen($size), 'unencrypted_size' => $size), '' ); \OC\Files\Filesystem::putFileInfo($filePath, array('encrypted' => true, 'size' => strlen($size), 'unencrypted_size' => $size), '');
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -174,56 +186,57 @@ class Proxy extends \OC_FileProxy {
* @param string $path Path of file from which has been read * @param string $path Path of file from which has been read
* @param string $data Data that has been read from file * @param string $data Data that has been read from file
*/ */
public function postFile_get_contents( $path, $data ) { public function postFile_get_contents($path, $data)
{
// FIXME: $path for shared files is just /uid/files/Shared/filepath // FIXME: $path for shared files is just /uid/files/Shared/filepath
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$relPath = $util->stripUserFilesPath( $path ); $relPath = $util->stripUserFilesPath($path);
// TODO check for existing key file and reuse it if possible to avoid problems with versioning etc. // TODO check for existing key file and reuse it if possible to avoid problems with versioning etc.
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// If data is a catfile // If data is a catfile
if ( if (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& Crypt::isCatfileContent( $data ) // TODO: Do we really need this check? Can't we assume it is properly encrypted? && Crypt::isCatfileContent($data) // TODO: Do we really need this check? Can't we assume it is properly encrypted?
) { ) {
// TODO: use get owner to find correct location of key files for shared files // TODO: use get owner to find correct location of key files for shared files
$session = new Session( $view ); $session = new Session($view);
$privateKey = $session->getPrivateKey( $userId ); $privateKey = $session->getPrivateKey($userId);
// Get the encrypted keyfile // Get the encrypted keyfile
$encKeyfile = Keymanager::getFileKey( $view, $userId, $relPath ); $encKeyfile = Keymanager::getFileKey($view, $userId, $relPath);
// Attempt to fetch the user's shareKey // Attempt to fetch the user's shareKey
$shareKey = Keymanager::getShareKey( $view, $userId, $relPath ); $shareKey = Keymanager::getShareKey($view, $userId, $relPath);
// Decrypt keyfile with shareKey // Decrypt keyfile with shareKey
$plainKeyfile = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
$plainData = Crypt::symmetricDecryptFileContent( $data, $plainKeyfile ); $plainData = Crypt::symmetricDecryptFileContent($data, $plainKeyfile);
} elseif ( } elseif (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& isset( $_SESSION['legacyenckey'] ) && isset($_SESSION['legacyenckey'])
&& Crypt::isEncryptedMeta( $path ) && Crypt::isEncryptedMeta($path)
) { ) {
$plainData = Crypt::legacyDecrypt( $data, $session->getLegacyKey() ); $plainData = Crypt::legacyDecrypt($data, $session->getLegacyKey());
} }
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( ! isset( $plainData ) ) { if (!isset($plainData)) {
$plainData = $data; $plainData = $data;
@ -236,37 +249,38 @@ class Proxy extends \OC_FileProxy {
/** /**
* @brief When a file is deleted, remove its keyfile also * @brief When a file is deleted, remove its keyfile also
*/ */
public function preUnlink( $path ) { public function preUnlink($path)
{
// let the trashbin handle this // let the trashbin handle this
if ( \OCP\App::isEnabled('files_trashbin') ) { if (\OCP\App::isEnabled('files_trashbin')) {
return true; return true;
} }
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
$userId = \OCP\USER::getUser(); $userId = \OCP\USER::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
// Format path to be relative to user files dir // Format path to be relative to user files dir
$relPath = $util->stripUserFilesPath( $path ); $relPath = $util->stripUserFilesPath($path);
list( $owner, $ownerPath ) = $util->getUidAndFilename( $relPath ); list($owner, $ownerPath) = $util->getUidAndFilename($relPath);
// Delete keyfile & shareKey so it isn't orphaned // Delete keyfile & shareKey so it isn't orphaned
if ( if (
! ( !(
Keymanager::deleteFileKey( $view, $owner, $ownerPath ) Keymanager::deleteFileKey($view, $owner, $ownerPath)
&& Keymanager::delAllShareKeys( $view, $owner, $ownerPath ) && Keymanager::delAllShareKeys($view, $owner, $ownerPath)
) )
) { ) {
\OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "'.$ownerPath.'"', \OC_Log::ERROR ); \OC_Log::write('Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OC_Log::ERROR);
} }
@ -279,97 +293,82 @@ class Proxy extends \OC_FileProxy {
} }
/** /**
* @brief When a file is renamed, rename its keyfile also * @brief When a file is renamed, rename its keyfile also
* @return bool Result of rename() * @param $path
* @note This is pre rather than post because using post didn't work * @return bool Result of rename()
*/ * @note This is pre rather than post because using post didn't work
public function postWrite( $path ) */
{ public function postWrite($path)
$this->handleFile($path); {
$this->handleFile($path);
return true; return true;
} }
public function postTouch( $path ) /**
{ * @param $path
$this->handleFile($path); * @return bool
*/
public function postTouch($path)
{
$this->handleFile($path);
return true; return true;
} }
public function postFopen( $path, &$result ){ /**
* @param $path
* @param $result
* @return resource
*/
public function postFopen($path, &$result)
{
if ( !$result ) { if (!$result) {
return $result; return $result;
} }
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode( '/', $path ); $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 // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
if($path_split[2] == 'cache') { if ($path_split[2] == 'cache') {
return $result; return $result;
} }
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$meta = stream_get_meta_data( $result ); $meta = stream_get_meta_data($result);
$view = new \OC_FilesystemView( '' ); $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 file is already encrypted, decrypt using crypto protocol
if ( if (
Crypt::mode() == 'server' Crypt::mode() == 'server'
&& $util->isEncryptedPath( $path ) && $util->isEncryptedPath($path)
) { ) {
// Close the original encrypted file // Close the original encrypted file
fclose( $result ); fclose($result);
// Open the file using the crypto stream wrapper // Open the file using the crypto stream wrapper
// protocol and let it do the decryption work instead // protocol and let it do the decryption work instead
$result = fopen( 'crypt://' . $path_f, $meta['mode'] ); $result = fopen('crypt://' . $path_f, $meta['mode']);
} elseif ( } elseif (
self::shouldEncrypt( $path ) self::shouldEncrypt($path)
and $meta ['mode'] != 'r' and $meta ['mode'] != 'r'
and $meta['mode'] != 'rb' and $meta['mode'] != 'rb'
) { ) {
// If the file is not yet encrypted, but should be $result = fopen('crypt://' . $path_f, $meta['mode']);
// 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'] );
} }
// Re-enable the proxy // Re-enable the proxy
@ -379,11 +378,17 @@ class Proxy extends \OC_FileProxy {
} }
public function postGetMimeType( $path, $mime ) { /**
* @param $path
* @param $mime
* @return string
*/
public function postGetMimeType($path, $mime)
{
if ( Crypt::isCatfileContent( $path ) ) { if (Crypt::isCatfileContent($path)) {
$mime = \OCP\Files::getMimeType( 'crypt://' . $path, 'w' ); $mime = \OCP\Files::getMimeType('crypt://' . $path, 'w');
} }
@ -391,113 +396,133 @@ class Proxy extends \OC_FileProxy {
} }
public function postGetFileInfo( $path, $data ) { /**
* @param $path
* @param $data
* @return array
*/
public function postGetFileInfo($path, $data)
{
// if path is a folder do nothing // if path is a folder do nothing
if(is_array($data) && array_key_exists('size', $data)) { if (is_array($data) && array_key_exists('size', $data)) {
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get file size // get file size
$data['size'] = self::postFileSize($path, $data['size']); $data['size'] = self::postFileSize($path, $data['size']);
// Re-enable the proxy // Re-enable the proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
} }
return $data; return $data;
} }
public function postStat($path, $data) /**
{ * @param $path
// check if file is encrypted * @param $data
if (Crypt::isCatfileContent($path)) { * @return mixed
*/
public function postStat($path, $data)
{
// check if file is encrypted
if (Crypt::isCatfileContent($path)) {
// get file info from cache // get file info from cache
$cached = \OC\Files\Filesystem::getFileInfo($path, ''); $cached = \OC\Files\Filesystem::getFileInfo($path, '');
// set the real file size // set the real file size
$data['size'] = $cached['unencrypted_size']; $data['size'] = $cached['unencrypted_size'];
} }
return $data; return $data;
} }
public function postFileSize($path, $size) /**
{ * @param $path
* @param $size
* @return bool
*/
public function postFileSize($path, $size)
{
$view = new \OC_FilesystemView('/'); $view = new \OC_FilesystemView('/');
// if path is a folder do nothing // if path is a folder do nothing
if ($view->is_dir($path)) { if ($view->is_dir($path)) {
return $size; return $size;
} }
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode('/', $path); $path_split = explode('/', $path);
$path_f = implode('/', array_slice($path_split, 3)); $path_f = implode('/', array_slice($path_split, 3));
// if path is empty we cannot resolve anything // if path is empty we cannot resolve anything
if(empty($path_f)) { if (empty($path_f)) {
return $size; return $size;
} }
// get file info from database/cache // get file info from database/cache
$fileInfo = \OC\Files\Filesystem::getFileInfo($path_f); $fileInfo = \OC\Files\Filesystem::getFileInfo($path_f);
// if file is encrypted return real file size // if file is encrypted return real file size
if (is_array($fileInfo) && $fileInfo['encrypted'] === true) { if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
$size = $fileInfo['unencrypted_size']; $size = $fileInfo['unencrypted_size'];
} else { } else {
// self healing if file was removed from file cache // self healing if file was removed from file cache
if(is_array($fileInfo)) { if (is_array($fileInfo)) {
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
$fixSize = $util->getFileSize($path); $fixSize = $util->getFileSize($path);
if($fixSize > 0) { if ($fixSize > 0) {
$size = $fixSize; $size = $fixSize;
$fileInfo['encrypted'] = true; $fileInfo['encrypted'] = true;
$fileInfo['unencrypted_size'] = $size; $fileInfo['unencrypted_size'] = $size;
// put file info // put file info
$view->putFileInfo( $path_f, $fileInfo ); $view->putFileInfo($path_f, $fileInfo);
} }
} }
} }
return $size; return $size;
} }
public function handleFile($path) { /**
* @param $path
*/
public function handleFile($path)
{
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView('/'); $view = new \OC_FilesystemView('/');
$session = new Session($view); $session = new Session($view);
$userId = \OCP\User::getUser(); $userId = \OCP\User::getUser();
$util = new Util( $view, $userId ); $util = new Util($view, $userId);
// Reformat path for use with OC_FSV // Reformat path for use with OC_FSV
$path_split = explode( '/', $path ); $path_split = explode('/', $path);
$path_f = implode( '/', array_slice( $path_split, 3 ) ); $path_f = implode('/', array_slice($path_split, 3));
// only if file is on 'files' folder fix file size and sharing // only if file is on 'files' folder fix file size and sharing
if($path_split[2] == 'files' && $util->fixFileSize($path)) { if ($path_split[2] == 'files' && $util->fixFileSize($path)) {
// get sharing app state // get sharing app state
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
// get users // get users
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path_f); $usersSharing = $util->getSharingUsersArray($sharingEnabled, $path_f);
// update sharing-keys // update sharing-keys
$util->setSharedFileKeyfiles($session, $usersSharing, $path_f); $util->setSharedFileKeyfiles($session, $usersSharing, $path_f);
} }
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
} }
} }

View File

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

View File

@ -48,14 +48,15 @@ namespace OCA\Encryption;
* previous version deleted, this is handled by OC\Files\View, and thus the * previous version deleted, this is handled by OC\Files\View, and thus the
* encryption proxies are used and keyfiles deleted. * encryption proxies are used and keyfiles deleted.
*/ */
class Stream { class Stream
{
public static $sourceStreams = array(); public static $sourceStreams = array();
private $plainKey;
private $encKeyfiles;
// TODO: make all below properties private again once unit testing is private $rawPath; // The raw path relative to the data dir
// configured correctly private $relPath; // rel path to users file dir
public $rawPath; // The raw path relative to the data dir
public $relPath; // rel path to users file dir
private $userId; private $userId;
private $handle; // Resource returned by fopen private $handle; // Resource returned by fopen
private $path; private $path;
@ -63,145 +64,154 @@ class Stream {
private $meta = array(); // Header / meta for source stream private $meta = array(); // Header / meta for source stream
private $count; private $count;
private $writeCache; private $writeCache;
public $size; private $size;
public $unencryptedSize; private $unencryptedSize;
private $publicKey; private $publicKey;
private $keyfile; private $keyfile;
private $encKeyfile; private $encKeyfile;
private static $view; // a fsview object set to user dir private static $view; // a fsview object set to user dir
private $rootView; // a fsview object set to '/' private $rootView; // a fsview object set to '/'
public function stream_open( $path, $mode, $options, &$opened_path ) { /**
* @param $path
* @param $mode
* @param $options
* @param $opened_path
* @return bool
*/
public function stream_open($path, $mode, $options, &$opened_path)
{
if ( ! isset( $this->rootView ) ) { if (!isset($this->rootView)) {
$this->rootView = new \OC_FilesystemView( '/' ); $this->rootView = new \OC_FilesystemView('/');
} }
$util = new Util( $this->rootView, \OCP\USER::getUser()); $util = new Util($this->rootView, \OCP\USER::getUser());
$this->userId = $util->getUserId(); $this->userId = $util->getUserId();
// Strip identifier text from path, this gives us the path relative to data/<user>/files // 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 )); $this->relPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path));
// rawPath is relative to the data directory // rawPath is relative to the data directory
$this->rawPath = $util->getUserFilesDir() . $this->relPath; $this->rawPath = $util->getUserFilesDir() . $this->relPath;
if ( if (
dirname( $this->rawPath ) == 'streams' dirname($this->rawPath) == 'streams'
and isset( self::$sourceStreams[basename( $this->rawPath )] ) and isset(self::$sourceStreams[basename($this->rawPath)])
) { ) {
// Is this just for unit testing purposes? // Is this just for unit testing purposes?
$this->handle = self::$sourceStreams[basename( $this->rawPath )]['stream']; $this->handle = self::$sourceStreams[basename($this->rawPath)]['stream'];
$this->path = self::$sourceStreams[basename( $this->rawPath )]['path']; $this->path = self::$sourceStreams[basename($this->rawPath)]['path'];
$this->size = self::$sourceStreams[basename( $this->rawPath )]['size']; $this->size = self::$sourceStreams[basename($this->rawPath)]['size'];
} else { } else {
// Disable fileproxies so we can get the file size and open the source file without recursive encryption // Disable fileproxies so we can get the file size and open the source file without recursive encryption
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
if ( if (
$mode == 'w' $mode == 'w'
or $mode == 'w+' or $mode == 'w+'
or $mode == 'wb' or $mode == 'wb'
or $mode == 'wb+' or $mode == 'wb+'
) { ) {
// We're writing a new file so start write counter with 0 bytes // We're writing a new file so start write counter with 0 bytes
$this->size = 0; $this->size = 0;
$this->unencryptedSize = 0; $this->unencryptedSize = 0;
} else { } else {
$this->size = $this->rootView->filesize( $this->rawPath, $mode ); $this->size = $this->rootView->filesize($this->rawPath, $mode);
//$this->size = filesize( $this->rawPath );
} }
//$this->handle = fopen( $this->rawPath, $mode ); $this->handle = $this->rootView->fopen($this->rawPath, $mode);
$this->handle = $this->rootView->fopen( $this->rawPath, $mode );
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
if ( ! is_resource( $this->handle ) ) { if (!is_resource($this->handle)) {
\OCP\Util::writeLog( 'files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR ); \OCP\Util::writeLog('files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR);
} else { } else {
$this->meta = stream_get_meta_data( $this->handle ); $this->meta = stream_get_meta_data($this->handle);
} }
} }
return is_resource( $this->handle ); return is_resource($this->handle);
} }
public function stream_seek( $offset, $whence = SEEK_SET ) { /**
* @param $offset
* @param int $whence
*/
public function stream_seek($offset, $whence = SEEK_SET)
{
$this->flush(); $this->flush();
fseek( $this->handle, $offset, $whence ); fseek($this->handle, $offset, $whence);
} }
public function stream_tell() { /**
* @return int
*/
public function stream_tell()
{
return ftell($this->handle); return ftell($this->handle);
} }
public function stream_read( $count ) { /**
* @param $count
* @return bool|string
* @throws \Exception
*/
public function stream_read($count)
{
$this->writeCache = ''; $this->writeCache = '';
if ( $count != 8192 ) { if ($count != 8192) {
// $count will always be 8192 https://bugs.php.net/bug.php?id=21641 // $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' // 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 ); \OCP\Util::writeLog('files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL);
die(); die();
} }
// $pos = ftell( $this->handle );
//
// Get the data from the file handle // Get the data from the file handle
$data = fread( $this->handle, 8192 ); $data = fread($this->handle, 8192);
$result = ''; $result = '';
if ( strlen( $data ) ) { if (strlen($data)) {
if ( ! $this->getKey() ) { if (!$this->getKey()) {
// Error! We don't have a key to decrypt the file with // 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' ); throw new \Exception('Encryption key not found for "' . $this->rawPath . '" during attempted read via stream');
} }
// Decrypt data // Decrypt data
$result = Crypt::symmetricDecryptFileContent( $data, $this->plainKey ); $result = Crypt::symmetricDecryptFileContent($data, $this->plainKey);
} }
// $length = $this->size - $pos;
//
// if ( $length < 8192 ) {
//
// $result = substr( $result, 0, $length );
//
// }
return $result; return $result;
} }
@ -210,12 +220,13 @@ class Stream {
* @brief Encrypt and pad data ready for writing to disk * @brief Encrypt and pad data ready for writing to disk
* @param string $plainData data to be encrypted * @param string $plainData data to be encrypted
* @param string $key key to use for encryption * @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 ) { public function preWriteEncrypt($plainData, $key)
{
// Encrypt data to 'catfile', which includes IV // Encrypt data to 'catfile', which includes IV
if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) { if ($encrypted = Crypt::symmetricEncryptFileContent($plainData, $key)) {
return $encrypted; return $encrypted;
@ -229,33 +240,34 @@ class Stream {
/** /**
* @brief Fetch the plain encryption key for the file and set it as plainKey property * @brief Fetch the plain encryption key for the file and set it as plainKey property
* @param bool $generate if true, a new key will be generated if none can be found * @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 * @return bool true on key found and set, false on key not found and new key generated and set
*/ */
public function getKey() { public function getKey()
{
// Check if key is already set // Check if key is already set
if ( isset( $this->plainKey ) && isset( $this->encKeyfile ) ) { if (isset($this->plainKey) && isset($this->encKeyfile)) {
return true; return true;
} }
// Fetch and decrypt keyfile // Fetch and decrypt keyfile
// Fetch existing keyfile // Fetch existing keyfile
$this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->relPath ); $this->encKeyfile = Keymanager::getFileKey($this->rootView, $this->userId, $this->relPath);
// If a keyfile already exists // If a keyfile already exists
if ( $this->encKeyfile ) { if ($this->encKeyfile) {
$this->setUserProperty(); $this->setUserProperty();
$session = new Session( $this->rootView ); $session = new Session($this->rootView);
$privateKey = $session->getPrivateKey( $this->userId ); $privateKey = $session->getPrivateKey($this->userId);
$shareKey = Keymanager::getShareKey( $this->rootView, $this->userId, $this->relPath ); $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $this->relPath);
$this->plainKey = Crypt::multiKeyDecrypt( $this->encKeyfile, $shareKey, $privateKey ); $this->plainKey = Crypt::multiKeyDecrypt($this->encKeyfile, $shareKey, $privateKey);
return true; return true;
@ -267,10 +279,11 @@ class Stream {
} }
public function setUserProperty() { public function setUserProperty()
{
// Only get the user again if it isn't already set // Only get the user again if it isn't already set
if ( empty( $this->userId ) ) { if (empty($this->userId)) {
// TODO: Move this user call out of here - it belongs // TODO: Move this user call out of here - it belongs
// elsewhere // elsewhere
@ -292,24 +305,25 @@ class Stream {
* @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read * @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read
* @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 * @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 ) { public function stream_write($data)
{
// Disable the file proxies so that encryption is not // Disable the file proxies so that encryption is not
// automatically attempted when the file is written to disk - // automatically attempted when the file is written to disk -
// we are handling that separately here and we don't want to // we are handling that separately here and we don't want to
// get into an infinite loop // get into an infinite loop
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Get the length of the unencrypted data that we are handling // Get the length of the unencrypted data that we are handling
$length = strlen( $data ); $length = strlen($data);
// So far this round, no data has been written // So far this round, no data has been written
$written = 0; $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 // file
$pointer = ftell( $this->handle ); $pointer = ftell($this->handle);
// Make sure the userId is set // Make sure the userId is set
$this->setUserProperty(); $this->setUserProperty();
@ -317,17 +331,16 @@ class Stream {
// Get / generate the keyfile for the file we're handling // Get / generate the keyfile for the file we're handling
// If we're writing a new file (not overwriting an existing // If we're writing a new file (not overwriting an existing
// one), save the newly generated keyfile // one), save the newly generated keyfile
if ( ! $this->getKey() ) { if (!$this->getKey()) {
$this->plainKey = Crypt::generateKey(); $this->plainKey = Crypt::generateKey();
} }
// If extra data is left over from the last round, make sure it // If extra data is left over from the last round, make sure it
// is integrated into the next 6126 / 8192 block // is integrated into the next 6126 / 8192 block
if ( $this->writeCache ) { if ($this->writeCache) {
// Concat writeCache to start of $data // Concat writeCache to start of $data
$data = $this->writeCache . $data; $data = $this->writeCache . $data;
@ -337,43 +350,18 @@ class Stream {
$this->writeCache = ''; $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->plainKey );
//
// $x = substr( $block, 0, $currentPos % 8192 );
//
// $data = $x . $data;
//
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
//
}
// $currentPos = ftell( $this->handle ); // While there still remains somed data to be processed & written
while (strlen($data) > 0) {
// // While there still remains somed data to be processed & written // Remaining length for this iteration, not of the
while( strlen( $data ) > 0 ) { // entire file (may be greater than 8192 bytes)
$remainingLength = strlen( $data );
// // Remaining length for this iteration, not of the // If data remaining to be written is less than the
// // entire file (may be greater than 8192 bytes) // size of 1 6126 byte block
// $remainingLength = strlen( $data ); if (strlen($data) < 6126) {
//
// // 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 // Set writeCache to contents of $data
// The writeCache will be carried over to the // The writeCache will be carried over to the
@ -391,79 +379,101 @@ class Stream {
} else { } else {
// Read the chunk from the start of $data // Read the chunk from the start of $data
$chunk = substr( $data, 0, 6126 ); $chunk = substr($data, 0, 6126);
$encrypted = $this->preWriteEncrypt( $chunk, $this->plainKey ); $encrypted = $this->preWriteEncrypt($chunk, $this->plainKey);
// Write the data chunk to disk. This will be // Write the data chunk to disk. This will be
// attended to the last data chunk if the file // attended to the last data chunk if the file
// being handled totals more than 6126 bytes // being handled totals more than 6126 bytes
fwrite( $this->handle, $encrypted ); fwrite($this->handle, $encrypted);
$writtenLen = strlen( $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 // $data, leaving only unprocessed data in $data
// var, for handling on the next round // var, for handling on the next round
$data = substr( $data, 6126 ); $data = substr($data, 6126);
} }
} }
$this->size = max( $this->size, $pointer + $length ); $this->size = max($this->size, $pointer + $length);
$this->unencryptedSize += $length; $this->unencryptedSize += $length;
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
return $length; return $length;
} }
public function stream_set_option( $option, $arg1, $arg2 ) { /**
switch($option) { * @param $option
* @param $arg1
* @param $arg2
*/
public function stream_set_option($option, $arg1, $arg2)
{
switch ($option) {
case STREAM_OPTION_BLOCKING: case STREAM_OPTION_BLOCKING:
stream_set_blocking( $this->handle, $arg1 ); stream_set_blocking($this->handle, $arg1);
break; break;
case STREAM_OPTION_READ_TIMEOUT: case STREAM_OPTION_READ_TIMEOUT:
stream_set_timeout( $this->handle, $arg1, $arg2 ); stream_set_timeout($this->handle, $arg1, $arg2);
break; break;
case STREAM_OPTION_WRITE_BUFFER: case STREAM_OPTION_WRITE_BUFFER:
stream_set_write_buffer( $this->handle, $arg1, $arg2 ); stream_set_write_buffer($this->handle, $arg1, $arg2);
} }
} }
public function stream_stat() { /**
* @return array
*/
public function stream_stat()
{
return fstat($this->handle); return fstat($this->handle);
} }
public function stream_lock( $mode ) { /**
flock( $this->handle, $mode ); * @param $mode
*/
public function stream_lock($mode)
{
flock($this->handle, $mode);
} }
public function stream_flush() { /**
* @return bool
*/
public function stream_flush()
{
return fflush( $this->handle ); return fflush($this->handle);
// Not a typo: http://php.net/manual/en/function.fflush.php // Not a typo: http://php.net/manual/en/function.fflush.php
} }
public function stream_eof() { /**
* @return bool
*/
public function stream_eof()
{
return feof($this->handle); return feof($this->handle);
} }
private function flush() { private function flush()
{
if ( $this->writeCache ) { if ($this->writeCache) {
// Set keyfile property for file in question // Set keyfile property for file in question
$this->getKey(); $this->getKey();
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->plainKey ); $encrypted = $this->preWriteEncrypt($this->writeCache, $this->plainKey);
fwrite( $this->handle, $encrypted ); fwrite($this->handle, $encrypted);
$this->writeCache = ''; $this->writeCache = '';
@ -471,63 +481,67 @@ class Stream {
} }
public function stream_close() { /**
* @return bool
*/
public function stream_close()
{
$this->flush(); $this->flush();
if ( if (
$this->meta['mode']!='r' $this->meta['mode'] != 'r'
and $this->meta['mode']!='rb' and $this->meta['mode'] != 'rb'
and $this->size > 0 and $this->size > 0
) { ) {
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// Fetch user's public key // Fetch user's public key
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId ); $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->userId);
// Check if OC sharing api is enabled // Check if OC sharing api is enabled
$sharingEnabled = \OCP\Share::isEnabled(); $sharingEnabled = \OCP\Share::isEnabled();
$util = new Util( $this->rootView, $this->userId ); $util = new Util($this->rootView, $this->userId);
// Get all users sharing the file includes current user // Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath, $this->userId); $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId);
// Fetch public keys for all sharing users // Fetch public keys for all sharing users
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds ); $publicKeys = Keymanager::getPublicKeys($this->rootView, $uniqueUserIds);
// Encrypt enc key for all sharing users // Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys ); $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys);
$view = new \OC_FilesystemView( '/' ); $view = new \OC_FilesystemView('/');
// Save the new encrypted file key // Save the new encrypted file key
Keymanager::setFileKey( $this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data'] ); Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']);
// Save the sharekeys // Save the sharekeys
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] ); Keymanager::setShareKeys($view, $this->relPath, $this->encKeyfiles['keys']);
// get file info // get file info
$fileInfo = $view->getFileInfo($this->rawPath); $fileInfo = $view->getFileInfo($this->rawPath);
if(!is_array($fileInfo)) { if (!is_array($fileInfo)) {
$fileInfo = array(); $fileInfo = array();
} }
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
// set encryption data // set encryption data
$fileInfo['encrypted'] = true; $fileInfo['encrypted'] = true;
$fileInfo['size'] = $this->size; $fileInfo['size'] = $this->size;
$fileInfo['unencrypted_size'] = $this->unencryptedSize; $fileInfo['unencrypted_size'] = $this->unencryptedSize;
// set fileinfo // set fileinfo
$view->putFileInfo( $this->rawPath, $fileInfo); $view->putFileInfo($this->rawPath, $fileInfo);
} }
return fclose( $this->handle ); return fclose($this->handle);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,21 +6,22 @@
* See the COPYING-README file. * See the COPYING-README file.
*/ */
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath( dirname(__FILE__).'/../lib/util.php' ); require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath( dirname(__FILE__).'/../lib/helper.php' ); require_once realpath(dirname(__FILE__) . '/../lib/helper.php');
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption; use OCA\Encryption;
/** /**
* Class Test_Encryption_Keymanager * Class Test_Encryption_Keymanager
*/ */
class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase
{
public $userId; public $userId;
public $pass; public $pass;
@ -31,38 +32,39 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
public $view; public $view;
public $randomKey; public $randomKey;
function setUp() { function setUp()
// reset backend {
\OC_User::clearBackends(); // reset backend
\OC_User::useBackend('database'); \OC_User::clearBackends();
\OC_User::useBackend('database');
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// set content for encrypting / decrypting in tests // set content for encrypting / decrypting in tests
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); $this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php'));
$this->dataShort = 'hats'; $this->dataShort = 'hats';
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); $this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php');
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); $this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt');
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); $this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt');
$this->randomKey = Encryption\Crypt::generateKey(); $this->randomKey = Encryption\Crypt::generateKey();
$keypair = Encryption\Crypt::createKeypair(); $keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey']; $this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey']; $this->genPrivateKey = $keypair['privateKey'];
$this->view = new \OC_FilesystemView( '/' ); $this->view = new \OC_FilesystemView('/');
\OC_User::setUserId( 'admin' ); \OC_User::setUserId('admin');
$this->userId = 'admin'; $this->userId = 'admin';
$this->pass = 'admin'; $this->pass = 'admin';
$userHome = \OC_User::getHome($this->userId); $userHome = \OC_User::getHome($this->userId);
$this->dataDir = str_replace('/'.$this->userId, '', $userHome); $this->dataDir = str_replace('/' . $this->userId, '', $userHome);
// Filesystem related hooks // Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks(); \OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy()); \OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state // remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
@ -70,18 +72,19 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
// we don't want to tests with app files_trashbin enabled // we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin'); \OC_App::disable('files_trashbin');
\OC_Util::tearDownFS(); \OC_Util::tearDownFS();
\OC_User::setUserId(''); \OC_User::setUserId('');
\OC\Files\Filesystem::tearDown(); \OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId); \OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId); \OC_User::setUserId($this->userId);
$params['uid'] = $this->userId; $params['uid'] = $this->userId;
$params['password'] = $this->pass; $params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params); OCA\Encryption\Hooks::login($params);
} }
function tearDown(){ function tearDown()
{
\OC_FileProxy::$enabled = true; \OC_FileProxy::$enabled = true;
\OC_FileProxy::clearProxies(); \OC_FileProxy::clearProxies();
@ -94,11 +97,12 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
} }
} }
function testGetPrivateKey() { function testGetPrivateKey()
{
$key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); $key = Encryption\Keymanager::getPrivateKey($this->view, $this->userId);
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->pass); $privateKey = Encryption\Crypt::symmetricDecryptFileContent($key, $this->pass);
$res = openssl_pkey_get_private($privateKey); $res = openssl_pkey_get_private($privateKey);
@ -110,9 +114,10 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
} }
function testGetPublicKey() { function testGetPublicKey()
{
$publiceKey = Encryption\Keymanager::getPublicKey( $this->view, $this->userId ); $publiceKey = Encryption\Keymanager::getPublicKey($this->view, $this->userId);
$res = openssl_pkey_get_public($publiceKey); $res = openssl_pkey_get_public($publiceKey);
@ -123,33 +128,34 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
$this->assertArrayHasKey('key', $sslInfo); $this->assertArrayHasKey('key', $sslInfo);
} }
function testSetFileKey() { function testSetFileKey()
{
# NOTE: This cannot be tested until we are able to break out # NOTE: This cannot be tested until we are able to break out
# of the FileSystemView data directory root # of the FileSystemView data directory root
$key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' ); $key = Encryption\Crypt::symmetricEncryptFileContentKeyfile($this->randomKey, 'hat');
$file = 'unittest-'.time().'.txt'; $file = 'unittest-' . time() . '.txt';
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']); $this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']);
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
//$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' ); //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' );
Encryption\Keymanager::setFileKey( $this->view, $file, $this->userId, $key['key'] ); Encryption\Keymanager::setFileKey($this->view, $file, $this->userId, $key['key']);
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = true; \OC_FileProxy::$enabled = true;
// cleanup // cleanup
$this->view->unlink('/'.$this->userId . '/files/' . $file); $this->view->unlink('/' . $this->userId . '/files/' . $file);
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
@ -172,9 +178,10 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
// //
// } // }
function testGetUserKeys() { function testGetUserKeys()
{
$keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId ); $keys = Encryption\Keymanager::getUserKeys($this->view, $this->userId);
$resPublic = openssl_pkey_get_public($keys['publicKey']); $resPublic = openssl_pkey_get_public($keys['publicKey']);
@ -184,7 +191,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
$this->assertArrayHasKey('key', $sslInfoPublic); $this->assertArrayHasKey('key', $sslInfoPublic);
$privateKey = Encryption\Crypt::symmetricDecryptFileContent( $keys['privateKey'], $this->pass); $privateKey = Encryption\Crypt::symmetricDecryptFileContent($keys['privateKey'], $this->pass);
$resPrivate = openssl_pkey_get_private($privateKey); $resPrivate = openssl_pkey_get_private($privateKey);

View File

@ -50,446 +50,446 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
public $subsubfolder; public $subsubfolder;
function setUp() function setUp()
{ {
// reset backend // reset backend
\OC_User::clearBackends(); \OC_User::clearBackends();
\OC_User::useBackend('database'); \OC_User::useBackend('database');
$this->dataShort = 'hats'; $this->dataShort = 'hats';
$this->view = new \OC_FilesystemView('/'); $this->view = new \OC_FilesystemView('/');
$userHome = \OC_User::getHome('admin'); $userHome = \OC_User::getHome('admin');
$this->dataDir = str_replace('/admin', '', $userHome); $this->dataDir = str_replace('/admin', '', $userHome);
$this->folder1 = '/folder1'; $this->folder1 = '/folder1';
$this->subfolder = '/subfolder1'; $this->subfolder = '/subfolder1';
$this->subsubfolder = '/subsubfolder1'; $this->subsubfolder = '/subsubfolder1';
$this->filename = 'share-tmp.test'; $this->filename = 'share-tmp.test';
// enable resharing // enable resharing
\OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes');
// clear share hooks // clear share hooks
\OC_Hook::clear('OCP\\Share'); \OC_Hook::clear('OCP\\Share');
\OC::registerShareHooks(); \OC::registerShareHooks();
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
// Sharing related hooks // Sharing related hooks
\OCA\Encryption\Helper::registerShareHooks(); \OCA\Encryption\Helper::registerShareHooks();
// Filesystem related hooks // Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks(); \OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy()); \OC_FileProxy::register(new OCA\Encryption\Proxy());
// remember files_trashbin state // remember files_trashbin state
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
// we don't want to tests with app files_trashbin enabled // we don't want to tests with app files_trashbin enabled
\OC_App::disable('files_trashbin'); \OC_App::disable('files_trashbin');
// create users // create users
$this->loginHelper('user1', true); $this->loginHelper('user1', true);
$this->loginHelper('user2', true); $this->loginHelper('user2', true);
$this->loginHelper('user3', true); $this->loginHelper('user3', true);
// create group and assign users // create group and assign users
\OC_Group::createGroup('group1'); \OC_Group::createGroup('group1');
\OC_Group::addToGroup('user2', 'group1'); \OC_Group::addToGroup('user2', 'group1');
\OC_Group::addToGroup('user3', 'group1'); \OC_Group::addToGroup('user3', 'group1');
} }
function tearDown() function tearDown()
{ {
// reset app files_trashbin // reset app files_trashbin
if ($this->stateFilesTrashbin) { if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin'); OC_App::enable('files_trashbin');
} else { } else {
OC_App::disable('files_trashbin'); OC_App::disable('files_trashbin');
} }
// clean group // clean group
\OC_Group::deleteGroup('group1'); \OC_Group::deleteGroup('group1');
// cleanup users // cleanup users
\OC_User::deleteUser('user1'); \OC_User::deleteUser('user1');
\OC_User::deleteUser('user2'); \OC_User::deleteUser('user2');
\OC_User::deleteUser('user3'); \OC_User::deleteUser('user3');
\OC_FileProxy::clearProxies(); \OC_FileProxy::clearProxies();
} }
/** /**
* @param bool $withTeardown * @param bool $withTeardown
*/ */
function testShareFile($withTeardown = true) function testShareFile($withTeardown = true)
{ {
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// save file with content // save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written // test that data was successfully written
$this->assertTrue(is_int($cryptedFile)); $this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls // disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get the file info from previous created file // get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename); $fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info // check if we have a valid file info
$this->assertTrue(is_array($fileInfo)); $this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored // check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']); $this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy // re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
// share the file // share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL); \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key for user1 exists // check if share key for user1 exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// get file contents // get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared/' . $this->filename); $retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared/' . $this->filename);
// check if data is the same as we previously written // check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup // cleanup
if ($withTeardown) { if ($withTeardown) {
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// unshare the file // unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup // cleanup
$this->view->unlink('/admin/files/' . $this->filename); $this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
} }
} }
/** /**
* @param bool $withTeardown * @param bool $withTeardown
*/ */
function testReShareFile($withTeardown = true) function testReShareFile($withTeardown = true)
{ {
$this->testShareFile(false); $this->testShareFile(false);
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// get the file info // get the file info
$fileInfo = $this->view->getFileInfo('/user1/files/Shared/' . $this->filename); $fileInfo = $this->view->getFileInfo('/user1/files/Shared/' . $this->filename);
// share the file with user2 // share the file with user2
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL); \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key for user2 exists // check if share key for user2 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 . '.user2.shareKey'));
// login as user2 // login as user2
$this->loginHelper('user2'); $this->loginHelper('user2');
// get file contents // get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename); $retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename);
// check if data is the same as previously written // check if data is the same as previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup // cleanup
if ($withTeardown) { if ($withTeardown) {
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// unshare the file with user2 // unshare the file with user2
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2'); \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key not exists // 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 . '.user2.shareKey'));
// unshare the file with user1 // unshare the file with user1
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey'));
// cleanup // cleanup
$this->view->unlink('/admin/files/' . $this->filename); $this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
} }
} }
/** /**
* @param bool $withTeardown * @param bool $withTeardown
* @return array * @return array
*/ */
function testShareFolder($withTeardown = true) function testShareFolder($withTeardown = true)
{ {
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// create folder structure // create folder structure
$this->view->mkdir('/admin/files' . $this->folder1); $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->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder); $this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder);
// save file with content // save file with content
$cryptedFile = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort); $cryptedFile = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename, $this->dataShort);
// test that data was successfully written // test that data was successfully written
$this->assertTrue(is_int($cryptedFile)); $this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls // disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get the file info from previous created folder // get the file info from previous created folder
$fileInfo = $this->view->getFileInfo('/admin/files' . $this->folder1); $fileInfo = $this->view->getFileInfo('/admin/files' . $this->folder1);
// check if we have a valid file info // check if we have a valid file info
$this->assertTrue(is_array($fileInfo)); $this->assertTrue(is_array($fileInfo));
// re-enable the file proxy // re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
// share the folder with user1 // share the folder with user1
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL); \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1', OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key for user1 exists // 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')); $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// get file contents // get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); $retrievedCryptedFile = $this->view->file_get_contents('/user1/files/Shared' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same // check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup // cleanup
if ($withTeardown) { if ($withTeardown) {
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// unshare the folder with user1 // unshare the folder with user1
\OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup // cleanup
$this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); $this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
} }
return $fileInfo; return $fileInfo;
} }
/** /**
* @param bool $withTeardown * @param bool $withTeardown
*/ */
function testReShareFolder($withTeardown = true) function testReShareFolder($withTeardown = true)
{ {
$fileInfoFolder1 = $this->testShareFolder(false); $fileInfoFolder1 = $this->testShareFolder(false);
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// disable encryption proxy to prevent recursive calls // disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get the file info from previous created folder // get the file info from previous created folder
$fileInfoSubFolder = $this->view->getFileInfo('/user1/files/Shared' . $this->folder1 . $this->subfolder); $fileInfoSubFolder = $this->view->getFileInfo('/user1/files/Shared' . $this->folder1 . $this->subfolder);
// check if we have a valid file info // check if we have a valid file info
$this->assertTrue(is_array($fileInfoSubFolder)); $this->assertTrue(is_array($fileInfoSubFolder));
// re-enable the file proxy // re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
// share the file with user2 // share the file with user2
\OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL); \OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key for user2 exists // 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')); $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as user2 // login as user2
$this->loginHelper('user2'); $this->loginHelper('user2');
// get file contents // get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename); $retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if data is the same // check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// get the file info // get the file info
$fileInfo = $this->view->getFileInfo('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename); $fileInfo = $this->view->getFileInfo('/user2/files/Shared' . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if we have fileInfos // check if we have fileInfos
$this->assertTrue(is_array($fileInfo)); $this->assertTrue(is_array($fileInfo));
// share the file with user3 // share the file with user3
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3', OCP\PERMISSION_ALL); \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3', OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// check if share key for user3 exists // 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')); $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user3 // login as user3
$this->loginHelper('user3'); $this->loginHelper('user3');
// get file contents // get file contents
$retrievedCryptedFile = $this->view->file_get_contents('/user3/files/Shared/' . $this->filename); $retrievedCryptedFile = $this->view->file_get_contents('/user3/files/Shared/' . $this->filename);
// check if data is the same // check if data is the same
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// cleanup // cleanup
if ($withTeardown) { if ($withTeardown) {
// login as user2 // login as user2
$this->loginHelper('user2'); $this->loginHelper('user2');
// unshare the file with user3 // unshare the file with user3
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3'); \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3');
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey'));
// login as user1 // login as user1
$this->loginHelper('user1'); $this->loginHelper('user1');
// unshare the folder with user2 // unshare the folder with user2
\OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2'); \OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2');
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey'));
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// unshare the folder1 with user1 // unshare the folder1 with user1
\OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); \OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1');
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey'));
// cleanup // cleanup
$this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); $this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key not exists // 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')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey'));
} }
} }
function testPublicShareFile() function testPublicShareFile()
{ {
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// save file with content // save file with content
$cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort);
// test that data was successfully written // test that data was successfully written
$this->assertTrue(is_int($cryptedFile)); $this->assertTrue(is_int($cryptedFile));
// disable encryption proxy to prevent recursive calls // disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
// get the file info from previous created file // get the file info from previous created file
$fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename); $fileInfo = $this->view->getFileInfo('/admin/files/' . $this->filename);
// check if we have a valid file info // check if we have a valid file info
$this->assertTrue(is_array($fileInfo)); $this->assertTrue(is_array($fileInfo));
// check if the unencrypted file size is stored // check if the unencrypted file size is stored
$this->assertGreaterThan(0, $fileInfo['unencrypted_size']); $this->assertGreaterThan(0, $fileInfo['unencrypted_size']);
// re-enable the file proxy // re-enable the file proxy
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
// share the file // share the file
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL); \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL);
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
$publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); $publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId');
// check if share key for public exists // check if share key for public exists
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey')); $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// some hacking to simulate public link // some hacking to simulate public link
$GLOBALS['app'] = 'files_sharing'; $GLOBALS['app'] = 'files_sharing';
$GLOBALS['fileOwner'] = 'admin'; $GLOBALS['fileOwner'] = 'admin';
\OC_User::setUserId(''); \OC_User::setUserId('');
// get file contents // get file contents
$retrievedCryptedFile = file_get_contents('crypt://' . $this->filename); $retrievedCryptedFile = file_get_contents('crypt://' . $this->filename);
// check if data is the same as we previously written // check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile); $this->assertEquals($this->dataShort, $retrievedCryptedFile);
// tear down // tear down
// login as admin // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
// unshare the file // unshare the file
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey'));
// cleanup // cleanup
$this->view->unlink('/admin/files/' . $this->filename); $this->view->unlink('/admin/files/' . $this->filename);
// check if share key not exists // check if share key not exists
$this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey'));
} }
function testShareFileWithGroup() function testShareFileWithGroup()
{ {
@ -581,7 +581,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// save file with content // save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort); $cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $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 // test that data was successfully written
$this->assertTrue(is_int($cryptedFile1)); $this->assertTrue(is_int($cryptedFile1));
@ -589,9 +589,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// check if share key for admin and recovery exists // 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 . '.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->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 . '.admin.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $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'));
// disable recovery for admin // disable recovery for admin
$this->assertTrue($util->setRecoveryForUser(0)); $this->assertTrue($util->setRecoveryForUser(0));
@ -600,8 +600,8 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
$util->removeRecoveryKeys('/'); $util->removeRecoveryKeys('/');
// check if share key for recovery not exists // 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->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->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
// enable recovery for admin // enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(1)); $this->assertTrue($util->setRecoveryForUser(1));
@ -610,16 +610,16 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
$util->addRecoveryKeys('/'); $util->addRecoveryKeys('/');
// check if share key for admin and recovery exists // 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->filename . '.' . $recoveryKeyId . '.shareKey'));
$this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $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 // cleanup
$this->view->unlink('/admin/files/' . $this->filename); $this->view->unlink('/admin/files/' . $this->filename);
$this->view->unlink('/admin/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename); $this->view->unlink('/admin/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
// check if share key for recovery not exists // 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->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->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey'));
} }
function testRecoveryForUser() function testRecoveryForUser()
@ -648,7 +648,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// save file with content // save file with content
$cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort); $cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort);
$cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $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 // test that data was successfully written
$this->assertTrue(is_int($cryptedFile1)); $this->assertTrue(is_int($cryptedFile1));
@ -656,9 +656,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// check if share key for user and recovery exists // 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 . '.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->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 . '.user1.shareKey'));
$this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.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 // login as admin
$this->loginHelper('admin'); $this->loginHelper('admin');
@ -671,7 +671,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// get file contents // get file contents
$retrievedCryptedFile1 = file_get_contents('crypt://' . $this->filename); $retrievedCryptedFile1 = file_get_contents('crypt://' . $this->filename);
$retrievedCryptedFile2 = file_get_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $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 // check if data is the same as we previously written
$this->assertEquals($this->dataShort, $retrievedCryptedFile1); $this->assertEquals($this->dataShort, $retrievedCryptedFile1);
@ -683,9 +683,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
// check if share key for user and recovery exists // 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 . '.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->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 . '.user1.shareKey'));
$this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder .'/'. $this->filename . '.'.$recoveryKeyId.'.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 // enable recovery for admin
$this->assertTrue($util->setRecoveryForUser(0)); $this->assertTrue($util->setRecoveryForUser(0));
@ -697,23 +697,23 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase
* @param bool $password * @param bool $password
*/ */
function loginHelper($user, $create = false, $password = false) function loginHelper($user, $create = false, $password = false)
{ {
if ($create) { if ($create) {
\OC_User::createUser($user, $user); \OC_User::createUser($user, $user);
} }
if($password === false) { if ($password === false) {
$password = $user; $password = $user;
} }
\OC_Util::tearDownFS(); \OC_Util::tearDownFS();
\OC_User::setUserId(''); \OC_User::setUserId('');
\OC\Files\Filesystem::tearDown(); \OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($user); \OC_Util::setupFS($user);
\OC_User::setUserId($user); \OC_User::setUserId($user);
$params['uid'] = $user; $params['uid'] = $user;
$params['password'] = $password; $params['password'] = $password;
OCA\Encryption\Hooks::login($params); OCA\Encryption\Hooks::login($params);
} }
} }

View File

@ -6,20 +6,21 @@
* See the COPYING-README file. * See the COPYING-README file.
*/ */
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); require_once realpath(dirname(__FILE__) . '/../../../lib/base.php');
require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); require_once realpath(dirname(__FILE__) . '/../lib/crypt.php');
require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php');
require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); require_once realpath(dirname(__FILE__) . '/../lib/proxy.php');
require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); require_once realpath(dirname(__FILE__) . '/../lib/stream.php');
require_once realpath( dirname(__FILE__).'/../lib/util.php' ); require_once realpath(dirname(__FILE__) . '/../lib/util.php');
require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); require_once realpath(dirname(__FILE__) . '/../appinfo/app.php');
use OCA\Encryption; use OCA\Encryption;
/** /**
* Class Test_Encryption_Util * Class Test_Encryption_Util
*/ */
class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { class Test_Encryption_Util extends \PHPUnit_Framework_TestCase
{
public $userId; public $userId;
public $encryptionDir; public $encryptionDir;
@ -38,56 +39,58 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
public $util; public $util;
public $dataShort; public $dataShort;
function setUp() { function setUp()
// reset backend {
\OC_User::useBackend('database'); // reset backend
\OC_User::useBackend('database');
\OC_User::setUserId( 'admin' ); \OC_User::setUserId('admin');
$this->userId = 'admin'; $this->userId = 'admin';
$this->pass = 'admin'; $this->pass = 'admin';
// set content for encrypting / decrypting in tests // set content for encrypting / decrypting in tests
$this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); $this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php');
$this->dataShort = 'hats'; $this->dataShort = 'hats';
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); $this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php'));
$this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); $this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt');
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); $this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt');
$keypair = Encryption\Crypt::createKeypair(); $keypair = Encryption\Crypt::createKeypair();
$this->genPublicKey = $keypair['publicKey']; $this->genPublicKey = $keypair['publicKey'];
$this->genPrivateKey = $keypair['privateKey']; $this->genPrivateKey = $keypair['privateKey'];
$this->publicKeyDir = '/' . 'public-keys'; $this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key $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->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
$this->view = new \OC_FilesystemView( '/' ); $this->view = new \OC_FilesystemView('/');
$userHome = \OC_User::getHome($this->userId); $userHome = \OC_User::getHome($this->userId);
$this->dataDir = str_replace('/'.$this->userId, '', $userHome); $this->dataDir = str_replace('/' . $this->userId, '', $userHome);
// Filesystem related hooks // Filesystem related hooks
\OCA\Encryption\Helper::registerFilesystemHooks(); \OCA\Encryption\Helper::registerFilesystemHooks();
\OC_FileProxy::register(new OCA\Encryption\Proxy()); \OC_FileProxy::register(new OCA\Encryption\Proxy());
\OC_Util::tearDownFS(); \OC_Util::tearDownFS();
\OC_User::setUserId(''); \OC_User::setUserId('');
\OC\Files\Filesystem::tearDown(); \OC\Files\Filesystem::tearDown();
\OC_Util::setupFS($this->userId); \OC_Util::setupFS($this->userId);
\OC_User::setUserId($this->userId); \OC_User::setUserId($this->userId);
$params['uid'] = $this->userId; $params['uid'] = $this->userId;
$params['password'] = $this->pass; $params['password'] = $this->pass;
OCA\Encryption\Hooks::login($params); OCA\Encryption\Hooks::login($params);
$this->util = new Encryption\Util( $this->view, $this->userId ); $this->util = new Encryption\Util($this->view, $this->userId);
} }
function tearDown(){ function tearDown()
{
\OC_FileProxy::clearProxies(); \OC_FileProxy::clearProxies();
} }
@ -95,75 +98,80 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
/** /**
* @brief test that paths set during User construction are correct * @brief test that paths set during User construction are correct
*/ */
function testKeyPaths() { function testKeyPaths()
{
$util = new Encryption\Util( $this->view, $this->userId ); $util = new Encryption\Util($this->view, $this->userId);
$this->assertEquals( $this->publicKeyDir, $util->getPath( 'publicKeyDir' ) ); $this->assertEquals($this->publicKeyDir, $util->getPath('publicKeyDir'));
$this->assertEquals( $this->encryptionDir, $util->getPath( 'encryptionDir' ) ); $this->assertEquals($this->encryptionDir, $util->getPath('encryptionDir'));
$this->assertEquals( $this->keyfilesPath, $util->getPath( 'keyfilesPath' ) ); $this->assertEquals($this->keyfilesPath, $util->getPath('keyfilesPath'));
$this->assertEquals( $this->publicKeyPath, $util->getPath( 'publicKeyPath' ) ); $this->assertEquals($this->publicKeyPath, $util->getPath('publicKeyPath'));
$this->assertEquals( $this->privateKeyPath, $util->getPath( 'privateKeyPath' ) ); $this->assertEquals($this->privateKeyPath, $util->getPath('privateKeyPath'));
} }
/** /**
* @brief test setup of encryption directories * @brief test setup of encryption directories
*/ */
function testSetupServerSide() { function testSetupServerSide()
{
$this->assertEquals( true, $this->util->setupServerSide( $this->pass ) ); $this->assertEquals(true, $this->util->setupServerSide($this->pass));
} }
/** /**
* @brief test checking whether account is ready for encryption, * @brief test checking whether account is ready for encryption,
*/ */
function testUserIsReady() { function testUserIsReady()
{
$this->assertEquals( true, $this->util->ready() ); $this->assertEquals(true, $this->util->ready());
} }
function testRecoveryEnabledForUser() { function testRecoveryEnabledForUser()
{
$util = new Encryption\Util( $this->view, $this->userId ); $util = new Encryption\Util($this->view, $this->userId);
// Record the value so we can return it to it's original state later // Record the value so we can return it to it's original state later
$enabled = $util->recoveryEnabledForUser(); $enabled = $util->recoveryEnabledForUser();
$this->assertTrue( $util->setRecoveryForUser( 1 ) ); $this->assertTrue($util->setRecoveryForUser(1));
$this->assertEquals( 1, $util->recoveryEnabledForUser() ); $this->assertEquals(1, $util->recoveryEnabledForUser());
$this->assertTrue( $util->setRecoveryForUser( 0 ) ); $this->assertTrue($util->setRecoveryForUser(0));
$this->assertEquals( 0, $util->recoveryEnabledForUser() ); $this->assertEquals(0, $util->recoveryEnabledForUser());
// Return the setting to it's previous state // Return the setting to it's previous state
$this->assertTrue( $util->setRecoveryForUser( $enabled ) ); $this->assertTrue($util->setRecoveryForUser($enabled));
} }
function testGetUidAndFilename() { function testGetUidAndFilename()
{
\OC_User::setUserId( 'admin' ); \OC_User::setUserId('admin');
$filename = 'tmp-'.time().'.test'; $filename = 'tmp-' . time() . '.test';
// Disable encryption proxy to prevent recursive calls // Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled; $proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false; \OC_FileProxy::$enabled = false;
$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort); $this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
// Re-enable proxy - our work is done // Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus; \OC_FileProxy::$enabled = $proxyStatus;
$util = new Encryption\Util( $this->view, $this->userId ); $util = new Encryption\Util($this->view, $this->userId);
list($fileOwnerUid, $file) = $util->getUidAndFilename( $filename ); list($fileOwnerUid, $file) = $util->getUidAndFilename($filename);
$this->assertEquals('admin', $fileOwnerUid); $this->assertEquals('admin', $fileOwnerUid);
$this->assertEquals($file, $filename); $this->assertEquals($file, $filename);
} }
} }