Implemented writing of keyfiles and directory hierarchy in proxy class
Added crypt::findFiles() method for finding different types of files, ready for batch encrypting / decrypting Added comments to postFopen in proxy class
This commit is contained in:
parent
ee15c40b14
commit
eebf76d344
6 changed files with 646 additions and 479 deletions
|
@ -37,14 +37,14 @@ class Hooks {
|
|||
|
||||
public static function login( $params ) {
|
||||
|
||||
if (Crypt::mode($params['uid'])=='server') {
|
||||
if ( Crypt::mode( $params['uid'] ) == 'server' ) {
|
||||
|
||||
$view = new \OC_FilesystemView( '/' );
|
||||
|
||||
$util = new Util( $view, $params['uid'] );
|
||||
|
||||
if ( !$util->ready()) {
|
||||
|
||||
|
||||
return $util->setupServerSide( $params['password'] );
|
||||
|
||||
}
|
||||
|
|
|
@ -1,420 +1,422 @@
|
|||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Sam Tuke, Frank Karlitschek, Robin Appelman
|
||||
* @copyright 2012 Sam Tuke samtuke@owncloud.com,
|
||||
* Robin Appelman icewind@owncloud.com, Frank Karlitschek
|
||||
* frank@owncloud.org
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA_Encryption;
|
||||
|
||||
/**
|
||||
* Class for common cryptography functionality
|
||||
*/
|
||||
|
||||
class Crypt {
|
||||
|
||||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Sam Tuke, Frank Karlitschek, Robin Appelman
|
||||
* @copyright 2012 Sam Tuke samtuke@owncloud.com,
|
||||
* Robin Appelman icewind@owncloud.com, Frank Karlitschek
|
||||
* frank@owncloud.org
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA_Encryption;
|
||||
|
||||
/**
|
||||
* Class for common cryptography functionality
|
||||
*/
|
||||
|
||||
class Crypt {
|
||||
|
||||
/**
|
||||
* @brief return encryption mode client or server side encryption
|
||||
* @brief return encryption mode client or server side encryption
|
||||
* @param string user name
|
||||
* @return string 'client' or 'server'
|
||||
*/
|
||||
public static function mode($user) {
|
||||
//TODO: allow user to set encryption mode and check the selection of the user
|
||||
public static function mode( $user ) {
|
||||
|
||||
//TODO: allow user to set encryption mode and check the selection of the user
|
||||
// for the moment I just return 'client' for test purposes
|
||||
return 'client';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new encryption keypair
|
||||
* @return array publicKey, privatekey
|
||||
*/
|
||||
public static function createKeypair() {
|
||||
|
||||
$res = openssl_pkey_new();
|
||||
|
||||
// Get private key
|
||||
openssl_pkey_export( $res, $privateKey );
|
||||
|
||||
// Get public key
|
||||
$publicKey = openssl_pkey_get_details( $res );
|
||||
|
||||
$publicKey = $publicKey['key'];
|
||||
|
||||
return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a file's contents contains an IV and is symmetrically encrypted
|
||||
* @return true / false
|
||||
*/
|
||||
public static function isEncryptedContent( $content ) {
|
||||
|
||||
if ( !$content ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Fetch encryption metadata from end of file
|
||||
$meta = substr( $content, -22 );
|
||||
|
||||
// Fetch IV from end of file
|
||||
$iv = substr( $meta, -16 );
|
||||
|
||||
// Fetch identifier from start of metadata
|
||||
$identifier = substr( $meta, 0, 6 );
|
||||
|
||||
if ( $identifier == '00iv00') {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a file is encrypted via legacy system
|
||||
* @return true / false
|
||||
*/
|
||||
public static function isLegacyEncryptedContent( $content, $path ) {
|
||||
|
||||
// Fetch all file metadata from DB
|
||||
$metadata = \OC_FileCache_Cached::get( $content, '' );
|
||||
|
||||
// If a file is flagged with encryption in DB, but isn't a valid content + IV combination, it's probably using the legacy encryption system
|
||||
if (
|
||||
$content
|
||||
and isset( $metadata['encrypted'] )
|
||||
and $metadata['encrypted'] === true
|
||||
and !self::isEncryptedContent( $content )
|
||||
) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically encrypt a string
|
||||
* @returns encrypted file
|
||||
*/
|
||||
public static function encrypt( $plainContent, $iv, $passphrase = '' ) {
|
||||
|
||||
if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
|
||||
|
||||
return $encryptedContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically decrypt a string
|
||||
* @returns decrypted file
|
||||
*/
|
||||
public static function decrypt( $encryptedContent, $iv, $passphrase ) {
|
||||
|
||||
if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (symmetric) of content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically encrypts a string and returns keyfile content
|
||||
* @param $plainContent content to be encrypted in keyfile
|
||||
* @returns encrypted content combined with IV
|
||||
* @note IV need not be specified, as it will be stored in the returned keyfile
|
||||
* and remain accessible therein.
|
||||
*/
|
||||
public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) {
|
||||
|
||||
if ( !$plainContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$iv = self::generateIv();
|
||||
|
||||
if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) {
|
||||
|
||||
// Combine content to encrypt with IV identifier and actual IV
|
||||
$combinedKeyfile = $encryptedContent . '00iv00' . $iv;
|
||||
|
||||
return $combinedKeyfile;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Symmetrically decrypts keyfile content
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $key the decryption key
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) {
|
||||
|
||||
if ( !$keyfileContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Fetch IV from end of file
|
||||
$iv = substr( $keyfileContent, -16 );
|
||||
|
||||
// Remove IV and IV identifier text to expose encrypted content
|
||||
$encryptedContent = substr( $keyfileContent, 0, -22 );
|
||||
|
||||
if ( $plainContent = self::decrypt( $encryptedContent, $iv, $passphrase ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (symmetric) of keyfile content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates symmetric keyfile content using a generated key
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function symmetricEncryptFileContentKeyfile( $plainContent ) {
|
||||
|
||||
$key = self::generateKey();
|
||||
|
||||
if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) {
|
||||
|
||||
return array(
|
||||
'key' => $key
|
||||
, 'encrypted' => $encryptedContent
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create asymmetrically encrypted keyfile content using a generated key
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function multiKeyEncrypt( $plainContent, array $publicKeys ) {
|
||||
|
||||
$envKeys = array();
|
||||
|
||||
if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) {
|
||||
|
||||
return array(
|
||||
'keys' => $envKeys
|
||||
, 'encrypted' => $sealed
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymmetrically encrypt a file using multiple public keys
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) {
|
||||
|
||||
if ( !$encryptedContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymetrically encrypt a string using a public key
|
||||
* @returns encrypted file
|
||||
*/
|
||||
public static function keyEncrypt( $plainContent, $publicKey ) {
|
||||
|
||||
openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey );
|
||||
|
||||
return $encryptedContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymetrically decrypt a file using a private key
|
||||
* @returns decrypted file
|
||||
*/
|
||||
public static function keyDecrypt( $encryptedContent, $privatekey ) {
|
||||
|
||||
openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
|
||||
|
||||
return $plainContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a pseudo random 1024kb ASCII key
|
||||
* @returns $key Generated key
|
||||
*/
|
||||
public static function generateIv() {
|
||||
|
||||
if ( $random = openssl_random_pseudo_bytes( 13, $strong ) ) {
|
||||
|
||||
if ( !$strong ) {
|
||||
|
||||
// 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 );
|
||||
|
||||
}
|
||||
|
||||
$iv = substr( base64_encode( $random ), 0, -4 );
|
||||
|
||||
return $iv;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a pseudo random 1024kb ASCII key
|
||||
* @returns $key Generated key
|
||||
*/
|
||||
public static function generateKey() {
|
||||
|
||||
// $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 );
|
||||
|
||||
// Generate key
|
||||
if ( $key = base64_encode( openssl_random_pseudo_bytes( 768000, $strong ) ) ) {
|
||||
|
||||
if ( !$strong ) {
|
||||
|
||||
// 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 );
|
||||
|
||||
}
|
||||
|
||||
return $key;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function changekeypasscode($oldPassword, $newPassword) {
|
||||
if(OCP\User::isLoggedIn()){
|
||||
$username=OCP\USER::getUser();
|
||||
$view=new OC_FilesystemView('/'.$username);
|
||||
|
||||
// read old key
|
||||
$key=$view->file_get_contents('/encryption.key');
|
||||
|
||||
// decrypt key with old passcode
|
||||
$key=OC_Crypt::decrypt($key, $oldPassword);
|
||||
|
||||
// encrypt again with new passcode
|
||||
$key=OC_Crypt::encrypt($key, $newPassword);
|
||||
|
||||
// store the new key
|
||||
$view->file_put_contents('/encryption.key', $key );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 'server';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new encryption keypair
|
||||
* @return array publicKey, privatekey
|
||||
*/
|
||||
public static function createKeypair() {
|
||||
|
||||
$res = openssl_pkey_new();
|
||||
|
||||
// Get private key
|
||||
openssl_pkey_export( $res, $privateKey );
|
||||
|
||||
// Get public key
|
||||
$publicKey = openssl_pkey_get_details( $res );
|
||||
|
||||
$publicKey = $publicKey['key'];
|
||||
|
||||
return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a file's contents contains an IV and is symmetrically encrypted
|
||||
* @return true / false
|
||||
*/
|
||||
public static function isEncryptedContent( $content ) {
|
||||
|
||||
if ( !$content ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Fetch encryption metadata from end of file
|
||||
$meta = substr( $content, -22 );
|
||||
|
||||
// Fetch IV from end of file
|
||||
$iv = substr( $meta, -16 );
|
||||
|
||||
// Fetch identifier from start of metadata
|
||||
$identifier = substr( $meta, 0, 6 );
|
||||
|
||||
if ( $identifier == '00iv00') {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a file is encrypted via legacy system
|
||||
* @return true / false
|
||||
*/
|
||||
public static function isLegacyEncryptedContent( $content, $path ) {
|
||||
|
||||
// Fetch all file metadata from DB
|
||||
$metadata = \OC_FileCache_Cached::get( $content, '' );
|
||||
|
||||
// If a file is flagged with encryption in DB, but isn't a valid content + IV combination, it's probably using the legacy encryption system
|
||||
if (
|
||||
$content
|
||||
and isset( $metadata['encrypted'] )
|
||||
and $metadata['encrypted'] === true
|
||||
and !self::isEncryptedContent( $content )
|
||||
) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically encrypt a string
|
||||
* @returns encrypted file
|
||||
*/
|
||||
public static function encrypt( $plainContent, $iv, $passphrase = '' ) {
|
||||
|
||||
if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
|
||||
|
||||
return $encryptedContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically decrypt a string
|
||||
* @returns decrypted file
|
||||
*/
|
||||
public static function decrypt( $encryptedContent, $iv, $passphrase ) {
|
||||
|
||||
if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (symmetric) of content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Symmetrically encrypts a string and returns keyfile content
|
||||
* @param $plainContent content to be encrypted in keyfile
|
||||
* @returns encrypted content combined with IV
|
||||
* @note IV need not be specified, as it will be stored in the returned keyfile
|
||||
* and remain accessible therein.
|
||||
*/
|
||||
public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) {
|
||||
|
||||
if ( !$plainContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$iv = self::generateIv();
|
||||
|
||||
if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) {
|
||||
|
||||
// Combine content to encrypt with IV identifier and actual IV
|
||||
$combinedKeyfile = $encryptedContent . '00iv00' . $iv;
|
||||
|
||||
return $combinedKeyfile;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Symmetrically decrypts keyfile content
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $key the decryption key
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) {
|
||||
|
||||
if ( !$keyfileContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Fetch IV from end of file
|
||||
$iv = substr( $keyfileContent, -16 );
|
||||
|
||||
// Remove IV and IV identifier text to expose encrypted content
|
||||
$encryptedContent = substr( $keyfileContent, 0, -22 );
|
||||
|
||||
if ( $plainContent = self::decrypt( $encryptedContent, $iv, $passphrase ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (symmetric) of keyfile content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates symmetric keyfile content using a generated key
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function symmetricEncryptFileContentKeyfile( $plainContent ) {
|
||||
|
||||
$key = self::generateKey();
|
||||
|
||||
if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) {
|
||||
|
||||
return array(
|
||||
'key' => $key
|
||||
, 'encrypted' => $encryptedContent
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create asymmetrically encrypted keyfile content using a generated key
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function multiKeyEncrypt( $plainContent, array $publicKeys ) {
|
||||
|
||||
$envKeys = array();
|
||||
|
||||
if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) {
|
||||
|
||||
return array(
|
||||
'keys' => $envKeys
|
||||
, 'encrypted' => $sealed
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymmetrically encrypt a file using multiple public keys
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @returns array keys: key, encrypted
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) {
|
||||
|
||||
if ( !$encryptedContent ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed' , \OC_Log::ERROR );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymetrically encrypt a string using a public key
|
||||
* @returns encrypted file
|
||||
*/
|
||||
public static function keyEncrypt( $plainContent, $publicKey ) {
|
||||
|
||||
openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey );
|
||||
|
||||
return $encryptedContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asymetrically decrypt a file using a private key
|
||||
* @returns decrypted file
|
||||
*/
|
||||
public static function keyDecrypt( $encryptedContent, $privatekey ) {
|
||||
|
||||
openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
|
||||
|
||||
return $plainContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a pseudo random 1024kb ASCII key
|
||||
* @returns $key Generated key
|
||||
*/
|
||||
public static function generateIv() {
|
||||
|
||||
if ( $random = openssl_random_pseudo_bytes( 13, $strong ) ) {
|
||||
|
||||
if ( !$strong ) {
|
||||
|
||||
// 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 );
|
||||
|
||||
}
|
||||
|
||||
$iv = substr( base64_encode( $random ), 0, -4 );
|
||||
|
||||
return $iv;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a pseudo random 1024kb ASCII key
|
||||
* @returns $key Generated key
|
||||
*/
|
||||
public static function generateKey() {
|
||||
|
||||
// $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 );
|
||||
|
||||
// Generate key
|
||||
if ( $key = base64_encode( openssl_random_pseudo_bytes( 768000, $strong ) ) ) {
|
||||
|
||||
if ( !$strong ) {
|
||||
|
||||
// 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 );
|
||||
|
||||
}
|
||||
|
||||
return $key;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function changekeypasscode($oldPassword, $newPassword) {
|
||||
if(OCP\User::isLoggedIn()){
|
||||
$username=OCP\USER::getUser();
|
||||
$view=new OC_FilesystemView('/'.$username);
|
||||
|
||||
// read old key
|
||||
$key=$view->file_get_contents('/encryption.key');
|
||||
|
||||
// decrypt key with old passcode
|
||||
$key=OC_Crypt::decrypt($key, $oldPassword);
|
||||
|
||||
// encrypt again with new passcode
|
||||
$key=OC_Crypt::encrypt($key, $newPassword);
|
||||
|
||||
// store the new key
|
||||
$view->file_put_contents('/encryption.key', $key );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -28,11 +28,11 @@
|
|||
*/
|
||||
|
||||
class OC_CryptStream{
|
||||
public static $sourceStreams=array();
|
||||
public static $sourceStreams = array();
|
||||
private $source;
|
||||
private $path;
|
||||
private $readBuffer;//for streams that dont support seeking
|
||||
private $meta=array();//header/meta for source stream
|
||||
private $readBuffer; // For streams that dont support seeking
|
||||
private $meta = array(); // Header / meta for source stream
|
||||
private $count;
|
||||
private $writeCache;
|
||||
private $size;
|
||||
|
@ -98,38 +98,69 @@ class OC_CryptStream{
|
|||
return $result;
|
||||
}
|
||||
|
||||
public function stream_write($data){
|
||||
$length=strlen($data);
|
||||
$written=0;
|
||||
$currentPos=ftell($this->source);
|
||||
if($this->writeCache){
|
||||
$data=$this->writeCache.$data;
|
||||
$this->writeCache='';
|
||||
public function stream_write( $data ){
|
||||
|
||||
$length = strlen( $data );
|
||||
|
||||
$written = 0;
|
||||
|
||||
$currentPos = ftell( $this->source );
|
||||
|
||||
if( $this->writeCache ){
|
||||
|
||||
$data = $this->writeCache.$data;
|
||||
|
||||
$this->writeCache = '';
|
||||
|
||||
}
|
||||
if($currentPos%8192!=0){
|
||||
|
||||
if( $currentPos%8192 != 0 ){
|
||||
|
||||
//make sure we always start on a block start
|
||||
fseek($this->source,-($currentPos%8192),SEEK_CUR);
|
||||
$encryptedBlock=fread($this->source,8192);
|
||||
fseek($this->source,-($currentPos%8192),SEEK_CUR);
|
||||
$block=OC_Crypt::decrypt($encryptedBlock);
|
||||
$data=substr($block,0,$currentPos%8192).$data;
|
||||
fseek($this->source,-($currentPos%8192),SEEK_CUR);
|
||||
|
||||
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
|
||||
|
||||
$encryptedBlock = fread( $this->source,8192 );
|
||||
|
||||
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
|
||||
|
||||
$block = OC_Crypt::decrypt( $encryptedBlock );
|
||||
|
||||
$data = substr( $block,0,$currentPos%8192 ).$data;
|
||||
|
||||
fseek( $this->source,-( $currentPos%8192 ),SEEK_CUR );
|
||||
|
||||
}
|
||||
$currentPos=ftell($this->source);
|
||||
while($remainingLength=strlen($data)>0){
|
||||
if($remainingLength<8192){
|
||||
$this->writeCache=$data;
|
||||
$data='';
|
||||
|
||||
$currentPos = ftell( $this->source );
|
||||
|
||||
while( $remainingLength = strlen( $data )>0 ){
|
||||
|
||||
if( $remainingLength<8192 ){
|
||||
|
||||
$this->writeCache = $data;
|
||||
|
||||
$data = '';
|
||||
|
||||
}else{
|
||||
$encrypted=OC_Crypt::encrypt(substr($data,0,8192));
|
||||
fwrite($this->source,$encrypted);
|
||||
$data=substr($data,8192);
|
||||
|
||||
$encrypted = OC_Crypt::encrypt( substr( $data,0,8192 ) );
|
||||
|
||||
fwrite( $this->source,$encrypted );
|
||||
|
||||
$data = substr( $data,8192 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$this->size=max($this->size,$currentPos+$length);
|
||||
|
||||
$this->size = max( $this->size,$currentPos+$length );
|
||||
|
||||
return $length;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function stream_set_option($option,$arg1,$arg2){
|
||||
switch($option){
|
||||
case STREAM_OPTION_BLOCKING:
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace OCA_Encryption;
|
|||
*/
|
||||
class Keymanager {
|
||||
|
||||
# TODO: Try and get rid of username dependencies as these methods need to be used in a proxy class that doesn't have username access
|
||||
# TODO: make all dependencies explicit, such as ocfsview objects, by adding them as method arguments (dependency injection)
|
||||
|
||||
/**
|
||||
* @brief retrieve private key from a user
|
||||
|
@ -60,9 +60,9 @@ class Keymanager {
|
|||
* @param string user name of the file owner
|
||||
* @return string file key or false
|
||||
*/
|
||||
public static function getFileKey($userId, $path) {
|
||||
public static function getFileKey( $userId, $path ) {
|
||||
|
||||
$keypath = ltrim($path, '/');
|
||||
$keypath = ltrim( $path, '/' );
|
||||
$user = $userId;
|
||||
|
||||
// update $keypath and $user if path point to a file shared by someone else
|
||||
|
@ -127,29 +127,33 @@ class Keymanager {
|
|||
* @param string $path relative path of the file, including filename
|
||||
* @param string $key
|
||||
* @return bool true/false
|
||||
*/
|
||||
*/
|
||||
public static function setFileKey( $userId, $path, $key ) {
|
||||
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$targetpath = ltrim($path, '/');
|
||||
$targetpath = ltrim( $path, '/' );
|
||||
$user = $userId;
|
||||
|
||||
// update $keytarget and $user if key belongs to a file shared by someone else
|
||||
$query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );
|
||||
$result = $query->execute( array ('/'.$userId.'/files/'.$targetpath, $userId));
|
||||
if ($row = $result->fetchRow()){
|
||||
|
||||
$result = $query->execute( array ( '/'.$userId.'/files/'.$targetpath, $userId ) );
|
||||
|
||||
if ( $row = $result->fetchRow( ) ) {
|
||||
$targetpath = $row['source'];
|
||||
$targetpath_parts=explode('/',$targetpath);
|
||||
$targetpath_parts=explode( '/',$targetpath );
|
||||
$user = $targetpath_parts[1];
|
||||
$targetpath = str_replace('/'.$user.'/files/', '', $targetpath);
|
||||
$targetpath = str_replace( '/'.$user.'/files/', '', $targetpath );
|
||||
//TODO: check for write permission on shared file once the new sharing API is in place
|
||||
}
|
||||
|
||||
$view = new \OC_FilesystemView( '/' . $user . '/files_encryption/keyfiles' );
|
||||
$path_parts = pathinfo($targetpath);
|
||||
|
||||
if (!$view->file_exists($path_parts['dirname'])) $view->mkdir($path_parts['dirname']);
|
||||
$path_parts = pathinfo( $targetpath );
|
||||
|
||||
if ( !$view->file_exists( $path_parts['dirname'] ) ) $view->mkdir( $path_parts['dirname'] );
|
||||
|
||||
$result = $view->file_put_contents( '/' . $targetpath . '.key', $key );
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
|
|
|
@ -109,10 +109,14 @@ class Proxy extends \OC_FileProxy {
|
|||
// Replace plain content with encrypted content by reference
|
||||
$data = $encrypted['encrypted'];
|
||||
|
||||
# TODO: check if file is in subdirectories, and if so, create those parent directories. Or else monitor creation of directories using hooks to ensure path will always exist (what about existing directories when encryption is enabled?)
|
||||
$filePath = explode( '/', $path );
|
||||
|
||||
// Save keyfile for newly encrypted file in parallel directory
|
||||
Keymanager::setFileKey( \OCP\USER::getUser(), $path, $encrypted['key'] );
|
||||
$filePath = array_slice( $filePath, 3 );
|
||||
|
||||
$filePath = '/' . implode( '/', $filePath );
|
||||
|
||||
// Save keyfile for newly encrypted file in parallel directory tree
|
||||
Keymanager::setFileKey( \OCP\USER::getUser(), $filePath, $encrypted['key'] );
|
||||
|
||||
// Update the file cache with file info
|
||||
\OC_FileCache::put( $path, array( 'encrypted'=>true, 'size' => $size ), '' );
|
||||
|
@ -124,38 +128,80 @@ class Proxy extends \OC_FileProxy {
|
|||
public function postFile_get_contents( $path, $data ) {
|
||||
|
||||
if ( Crypt::isEncryptedContent( $data ) ) {
|
||||
trigger_error('best');
|
||||
|
||||
$filePath = explode( '/', $path );
|
||||
|
||||
$filePath = array_slice( $filePath, 3 );
|
||||
|
||||
$filePath = '/' . implode( '/', $filePath );
|
||||
|
||||
trigger_error( "CAT " . $filePath);
|
||||
|
||||
$cached = \OC_FileCache_Cached::get( $path, '' );
|
||||
|
||||
$data = Crypt::symmetricDecryptFileContent( $data, $_SESSION['enckey'] );
|
||||
// Get keyfile for encrypted file
|
||||
$keyFile = Keymanager::getFileKey( \OCP\USER::getUser(), $filePath );
|
||||
|
||||
$data = Crypt::symmetricDecryptFileContent( $data, $keyFile );
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
public function postFopen($path,&$result){
|
||||
public function postFopen( $path, &$result ){
|
||||
|
||||
if(!$result){
|
||||
if ( !$result ) {
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
$meta=stream_get_meta_data($result);
|
||||
if(Crypt::isEncryptedContent($path)){
|
||||
fclose($result);
|
||||
$result=fopen('crypt://'.$path,$meta['mode']);
|
||||
}elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb'){
|
||||
if( \OC_Filesystem::file_exists( $path ) and \OC_Filesystem::filesize($path)>0){
|
||||
|
||||
$meta = stream_get_meta_data( $result );
|
||||
|
||||
// If file is encrypted, decrypt using crypto protocol
|
||||
if ( Crypt::isEncryptedContent( $path ) ) {
|
||||
|
||||
fclose ( $result );
|
||||
|
||||
$result = fopen( 'crypt://'.$path, $meta['mode'] );
|
||||
|
||||
} elseif (
|
||||
self::shouldEncrypt( $path )
|
||||
and $meta ['mode'] != 'r'
|
||||
and $meta['mode'] != 'rb'
|
||||
) {
|
||||
|
||||
# TODO: figure out what this does
|
||||
|
||||
if (
|
||||
\OC_Filesystem::file_exists( $path )
|
||||
and \OC_Filesystem::filesize( $path ) > 0
|
||||
) {
|
||||
|
||||
//first encrypt the target file so we don't end up with a half encrypted file
|
||||
\OCP\Util::writeLog('files_encryption','Decrypting '.$path.' before writing', \OCP\Util::DEBUG);
|
||||
$tmp=fopen('php://temp');
|
||||
\OCP\Files::streamCopy($result,$tmp);
|
||||
fclose($result);
|
||||
\OC_Filesystem::file_put_contents($path,$tmp);
|
||||
fclose($tmp);
|
||||
\OCP\Util::writeLog( 'files_encryption', 'Decrypting '.$path.' before writing', \OCP\Util::DEBUG );
|
||||
|
||||
$tmp = fopen( 'php://temp' );
|
||||
|
||||
\OCP\Files::streamCopy( $result, $tmp );
|
||||
|
||||
// Close the original stream, we'll return another one
|
||||
fclose( $result );
|
||||
|
||||
\OC_Filesystem::file_put_contents( $path, $tmp );
|
||||
|
||||
fclose( $tmp );
|
||||
|
||||
}
|
||||
$result=fopen('crypt://'.$path,$meta['mode']);
|
||||
|
||||
$result = fopen( 'crypt://'.$path, $meta['mode'] );
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function postGetMimeType($path,$mime){
|
||||
|
|
|
@ -44,18 +44,19 @@ class Util {
|
|||
# DONE: add method to check if file is encrypted using old system
|
||||
# DONE: add method to fetch legacy key
|
||||
# DONE: add method to decrypt legacy encrypted data
|
||||
# DONE: fix / test the crypt stream proxy class
|
||||
|
||||
# TODO: replace cryptstream wrapper with stream_socket_enable_crypto, or fix it to use new crypt class methods
|
||||
# TODO: add support for optional recovery user in case of lost passphrase / keys
|
||||
# TODO: add admin optional required long passphrase for users
|
||||
# TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
||||
# TODO: add UI buttons for encrypt / decrypt everything?
|
||||
|
||||
# TODO: add method to encrypt all user files using new system
|
||||
# TODO: add method to decrypt all user files using new system
|
||||
# TODO: add method to encrypt all user files using old system
|
||||
# TODO: add method to decrypt all user files using old system
|
||||
|
||||
# TODO: fix / test the crypt stream proxy class
|
||||
# TODO: add support for optional recovery user in case of lost passphrase / keys
|
||||
# TODO: add admin optional required long passphrase for users
|
||||
# TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
||||
# TODO: add UI buttons for encrypt / decrypt everything?
|
||||
|
||||
# TODO: test new encryption with webdav
|
||||
# TODO: test new encryption with versioning
|
||||
# TODO: test new encryption with sharing
|
||||
|
@ -154,6 +155,89 @@ class Util {
|
|||
|
||||
}
|
||||
|
||||
public function findFiles( $directory, $type = 'plain' ) {
|
||||
|
||||
# TODO: test finding non plain content
|
||||
|
||||
if ( $handle = $this->view->opendir( $directory ) ) {
|
||||
|
||||
while ( false !== ( $file = readdir( $handle ) ) ) {
|
||||
|
||||
if (
|
||||
$file != "."
|
||||
&& $file != ".."
|
||||
) {
|
||||
|
||||
$filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file );
|
||||
|
||||
var_dump($filePath);
|
||||
|
||||
if ( $this->view->is_dir( $filePath ) ) {
|
||||
|
||||
$this->findFiles( $filePath );
|
||||
|
||||
} elseif ( $this->view->is_file( $filePath ) ) {
|
||||
|
||||
if ( $type == 'plain' ) {
|
||||
|
||||
$this->files[] = array( 'name' => $file, 'path' => $filePath );
|
||||
|
||||
} elseif ( $type == 'encrypted' ) {
|
||||
|
||||
if ( Crypt::isEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
|
||||
|
||||
$this->files[] = array( 'name' => $file, 'path' => $filePath );
|
||||
|
||||
}
|
||||
|
||||
} elseif ( $type == 'legacy' ) {
|
||||
|
||||
if ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
|
||||
|
||||
$this->files[] = array( 'name' => $file, 'path' => $filePath );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( !empty( $this->files ) ) {
|
||||
|
||||
return $this->files;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function encryptAll( OC_FilesystemView $view ) {
|
||||
|
||||
$plainFiles = $this->findPlainFiles( $view );
|
||||
|
||||
if ( $this->encryptFiles( $plainFiles ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the blowfish encryption handeler for a key
|
||||
* @param $key string (optional)
|
||||
|
|
Loading…
Reference in a new issue