&& OCA\Encryption\Crypt::mode() == 'server'
) {
- // Force the user to re-log in if the encryption key isn't unlocked
+ // Force the user to log-in again if the encryption key isn't unlocked
// (happens when a user is logged in before the encryption app is
// enabled)
OCP\User::logout();
}
OCP\App::registerAdmin( 'files_encryption', 'settings' );
-OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );
\ No newline at end of file
+
+// This is disabled until client-side encryption is supported:
+// OCP\App::registerPersonal( 'files_encryption', 'settings-personal' );
\ No newline at end of file
* @return true / false\r
* @note see also OCA\Encryption\Util->isEncryptedPath()\r
*/\r
- public static function isEncryptedContent( $content ) {\r
+ public static function isCatfile( $content ) {\r
\r
if ( !$content ) {\r
\r
$content\r
and isset( $metadata['encrypted'] ) \r
and $metadata['encrypted'] === true \r
- and !self::isEncryptedContent( $content ) \r
+ and !self::isCatfile( $content ) \r
) {\r
\r
return true;\r
\r
}\r
\r
+ /**\r
+ * @brief store file encryption key\r
+ *\r
+ * @param string $path relative path of the file, including filename\r
+ * @param string $key\r
+ * @return bool true/false\r
+ * @note The keyfile is not encrypted here. Client code must \r
+ * asymmetrically encrypt the keyfile before passing it to this method\r
+ */\r
+ public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) {\r
+ \r
+ $basePath = '/' . $userId . '/files_encryption/keyfiles';\r
+ \r
+ $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId );\r
+ \r
+// // update $keytarget and $userId if key belongs to a file shared by someone else\r
+// $query = $dbClassName::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );\r
+// \r
+// $result = $query->execute( array ( '/'.$userId.'/files/'.$targetPath, $userId ) );\r
+// \r
+// if ( $row = $result->fetchRow( ) ) {\r
+// \r
+// $targetPath = $row['source'];\r
+// \r
+// $targetPath_parts = explode( '/', $targetPath );\r
+// \r
+// $userId = $targetPath_parts[1];\r
+// \r
+// $rootview = new \OC_FilesystemView( '/' );\r
+// \r
+// if ( ! $rootview->is_writable( $targetPath ) ) {\r
+// \r
+// \OC_Log::write( 'Encryption library', "File Key not updated because you don't have write access for the corresponding file", \OC_Log::ERROR );\r
+// \r
+// return false;\r
+// \r
+// }\r
+// \r
+// $targetPath = str_replace( '/'.$userId.'/files/', '', $targetPath );\r
+// \r
+// //TODO: check for write permission on shared file once the new sharing API is in place\r
+// \r
+// }\r
+ \r
+ // Save the keyfile in parallel directory\r
+ return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );\r
+ \r
+ }\r
+ \r
/**\r
* @brief retrieve keyfile for an encrypted file\r
* @param string file name\r
- * @return string file key or false\r
+ * @return string file key or false on failure\r
* @note The keyfile returned is asymmetrically encrypted. Decryption\r
* of the keyfile must be performed by client code\r
*/\r
// \r
// }\r
\r
- return $view->file_get_contents( '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key' );\r
+ $catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key';\r
+ \r
+ if ( $view->file_exists( $catfilePath ) ) {\r
+\r
+ return $view->file_get_contents( $catfilePath );\r
+ \r
+ } else {\r
+ \r
+ return false;\r
+ \r
+ }\r
\r
}\r
\r
\r
}\r
\r
- /**\r
- * @brief store file encryption key\r
- *\r
- * @param string $path relative path of the file, including filename\r
- * @param string $key\r
- * @return bool true/false\r
- * @note The keyfile is not encrypted here. Client code must \r
- * asymmetrically encrypt the keyfile before passing it to this method\r
- */\r
- public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) {\r
- \r
- $basePath = '/' . $userId . '/files_encryption/keyfiles';\r
- \r
- $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId );\r
- \r
-// // update $keytarget and $userId if key belongs to a file shared by someone else\r
-// $query = $dbClassName::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );\r
-// \r
-// $result = $query->execute( array ( '/'.$userId.'/files/'.$targetPath, $userId ) );\r
-// \r
-// if ( $row = $result->fetchRow( ) ) {\r
-// \r
-// $targetPath = $row['source'];\r
-// \r
-// $targetPath_parts = explode( '/', $targetPath );\r
-// \r
-// $userId = $targetPath_parts[1];\r
-// \r
-// $rootview = new \OC_FilesystemView( '/' );\r
-// \r
-// if ( ! $rootview->is_writable( $targetPath ) ) {\r
-// \r
-// \OC_Log::write( 'Encryption library', "File Key not updated because you don't have write access for the corresponding file", \OC_Log::ERROR );\r
-// \r
-// return false;\r
-// \r
-// }\r
-// \r
-// $targetPath = str_replace( '/'.$userId.'/files/', '', $targetPath );\r
-// \r
-// //TODO: check for write permission on shared file once the new sharing API is in place\r
-// \r
-// }\r
- \r
- // Save the keyfile in parallel directory\r
- return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile );\r
- \r
- }\r
- \r
/**\r
* @brief change password of private encryption key\r
*\r
}
- if ( Crypt::isEncryptedContent( $path ) ) {
+ if ( Crypt::isCatfile( $path ) ) {
return true;
// If data is a catfile
if (
Crypt::mode() == 'server'
- && Crypt::isEncryptedContent( $data )
+ && Crypt::isCatfile( $data )
) {
$split = explode( '/', $path );
}
public function postGetMimeType($path,$mime){
- if( Crypt::isEncryptedContent($path)){
+ if( Crypt::isCatfile($path)){
$mime = \OCP\Files::getMimeType('crypt://'.$path,'w');
}
return $mime;
}
public function postStat($path,$data){
- if( Crypt::isEncryptedContent($path)){
+ if( Crypt::isCatfile($path)){
$cached= \OC_FileCache_Cached::get($path,'');
$data['size']=$cached['size'];
}
}
public function postFileSize($path,$size){
- if( Crypt::isEncryptedContent($path)){
+ if( Crypt::isCatfile($path)){
$cached = \OC_FileCache_Cached::get($path,'');
return $cached['size'];
}else{
private $view; // OC_FilesystemView object for filesystem operations
+ private $userId; // ID of the currently logged-in user
private $pwd; // User Password
private $client; // Client side encryption mode flag
private $publicKeyDir; // Dir containing all public user keys
$this->view = $view;
$this->userId = $userId;
$this->client = $client;
+ $this->userDir = '/' . $this->userId;
+ $this->userFilesDir = '/' . $this->userId . '/' . 'files';
$this->publicKeyDir = '/' . 'public-keys';
$this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption';
$this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles';
public function ready() {
if(
- !$this->view->file_exists( $this->keyfilesPath )
+ !$this->view->file_exists( $this->encryptionDir )
+ or !$this->view->file_exists( $this->keyfilesPath )
+ or !$this->view->file_exists( $this->shareKeysPath )
or !$this->view->file_exists( $this->publicKeyPath )
or !$this->view->file_exists( $this->privateKeyPath )
) {
*/
public function setupServerSide( $passphrase = null ) {
+ // Create user dir
+ if( !$this->view->file_exists( $this->userDir ) ) {
+
+ $this->view->mkdir( $this->userDir );
+
+ }
+
+ // Create user files dir
+ if( !$this->view->file_exists( $this->userFilesDir ) ) {
+
+ $this->view->mkdir( $this->userFilesDir );
+
+ }
+
// Create shared public key directory
if( !$this->view->file_exists( $this->publicKeyDir ) ) {
// Create user keypair
if (
- !$this->view->file_exists( $this->publicKeyPath )
- or !$this->view->file_exists( $this->privateKeyPath )
+ ! $this->view->file_exists( $this->publicKeyPath )
+ or ! $this->view->file_exists( $this->privateKeyPath )
) {
// Generate keypair
$keypair = Crypt::createKeypair();
-
+
\OC_FileProxy::$enabled = false;
// Save public key
}
+ $publicKey = Keymanager::getPublicKey( $this->view, $this->userId );
+
+ // Encrypt existing user files:
+ $this->encryptAll( $publicKey, $this->userFilesDir );
+
return true;
}
- public function findFiles( $directory, $type = 'plain' ) {
-
- # TODO: test finding non plain content
+ /**
+ * @brief Find all files and their encryption status within a directory
+ * @param string $directory The path of the parent directory to search
+ * @return mixed false if 0 found, array on success. Keys: name, path
+ */
+ public function findFiles( $directory ) {
+
+ // Disable proxy - we don't want files to be decrypted before
+ // we handle them
+ \OC_FileProxy::$enabled = false;
+
+ $found = array( 'plain' => array(), 'encrypted' => array(), 'legacy' => array() );
+
+ if (
+ $this->view->is_dir( $directory )
+ && $handle = $this->view->opendir( $directory )
+ ) {
- if ( $handle = $this->view->opendir( $directory ) ) {
-
while ( false !== ( $file = readdir( $handle ) ) ) {
-
+
if (
$file != "."
&& $file != ".."
) {
-
- $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file );
- var_dump($filePath);
+ $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file );
+ // If the path is a directory, search
+ // its contents
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 the path is a file, determine
+ // its encryption status
+ } elseif ( $this->view->is_file( $filePath ) ) {
- if ( Crypt::isEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
-
- $this->files[] = array( 'name' => $file, 'path' => $filePath );
-
- }
+ // Disable proxies again, some-
+ // how they get re-enabled :/
+ \OC_FileProxy::$enabled = false;
- } elseif ( $type == 'legacy' ) {
+ // If the file is encrypted
+ if ( Keymanager::getFileKey( $this->view, $this->userId, $file ) ) {
- if ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
+ $found['encrypted'][] = array( 'name' => $file, 'path' => $filePath );
+
+ // If the file uses old
+ // encryption system
+ } elseif ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) {
- $this->files[] = array( 'name' => $file, 'path' => $filePath );
+ $found['legacy'][] = array( 'name' => $file, 'path' => $filePath );
- }
+ // If the file is not encrypted
+ } else {
+
+ $found['plain'][] = array( 'name' => $file, 'path' => $filePath );
}
}
- if ( !empty( $this->files ) ) {
+ \OC_FileProxy::$enabled = true;
- return $this->files;
+ if ( empty( $found ) ) {
+
+ return false;
} else {
- return false;
+ return $found;
}
}
+ \OC_FileProxy::$enabled = true;
+
return false;
}
\OC_FileProxy::$enabled = true;
- return Crypt::isEncryptedContent( $data );
+ return Crypt::isCatfile( $data );
}
- public function encryptAll( $directory ) {
+ /**
+ * @brief Encrypt all files in a directory
+ * @param string $publicKey the public key to encrypt files with
+ * @param string $dirPath the directory whose files will be encrypted
+ * @note Encryption is recursive
+ */
+ public function encryptAll( $publicKey, $dirPath ) {
- $plainFiles = $this->findFiles( $this->view, 'plain' );
+ if ( $found = $this->findFiles( $dirPath ) ) {
- if ( $this->encryptFiles( $plainFiles ) ) {
-
- return true;
+ // Encrypt unencrypted files
+ foreach ( $found['plain'] as $plainFilePath ) {
- } else {
-
- return false;
+ // Fetch data from file
+ $plainData = $this->view->file_get_contents( $plainFilePath );
+
+ // Encrypt data, generate catfile
+ $encrypted = Crypt::keyEncryptKeyfile( $plainData, $publicKey );
+
+ // Save catfile
+ Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $encrypted['key'] );
+
+ // Overwrite the existing file with the encrypted one
+ $this->view->file_put_contents( $plainFilePath, $encrypted['data'] );
+
+ }
+ // FIXME: Legacy recrypting here isn't finished yet
+ // Encrypt legacy encrypted files
+ foreach ( $found['legacy'] as $legacyFilePath ) {
+
+ // Fetch data from file
+ $legacyData = $this->view->file_get_contents( $legacyFilePath );
+
+ // Recrypt data, generate catfile
+ $recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKey, $newPassphrase );
+
+ // Save catfile
+ Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $recrypted['key'] );
+
+ // Overwrite the existing file with the encrypted one
+ $this->view->file_put_contents( $plainFilePath, $recrypted['data'] );
+
+ }
+
}
}
value="<?php echo $_['encryption_mode']; ?>"\r
>\r
\r
- <input \r
- type="radio" \r
- name="encryption_mode" \r
- value="client" \r
- id='client_encryption' \r
- style="width:20px;" \r
- <?php if ($_['encryption_mode'] == 'client') echo "checked='checked'"?>\r
- /> \r
- <?php echo $l->t('Client side encryption (most secure but makes it impossible to access your data from the web interface)'); ?>\r
- <br />\r
- \r
<input \r
type="radio" \r
name="encryption_mode" \r
id='server_encryption' \r
style="width:20px;" <?php if ($_['encryption_mode'] == 'server') echo "checked='checked'"?>\r
/> \r
- <?php echo $l->t('Server side encryption (allows you to access your files from the web interface and the desktop client)'); ?>\r
+ <?php echo $l->t('Server side encryption (allows you to access your files from the web interface)'); ?>\r
<br />\r
\r
<input \r
<form id="encryption">
<fieldset class="personalblock">
- <strong>
- <?php echo $l->t('Choose encryption mode:'); ?>
- </strong>
-
- <p>
- <i>
- <?php echo $l->t('Important: Once you selected an encryption mode there is no way to change it back'); ?>
- </i>
- </p>
-
- <p>
- <input
- type="radio"
- name="encryption_mode"
- id="client_encryption"
- value="client"
- style="width:20px;"
- <?php if ($_['encryption_mode'] == 'client') echo "checked='checked'"; if ($_['encryption_mode'] != 'none') echo "DISABLED"?>
- />
-
- <?php echo $l->t("Client side encryption (most secure but makes it impossible to access your data from the web interface)"); ?>
- <br />
-
- <input
- type="radio"
- name="encryption_mode"
- id="server_encryption"
- value="server"
- style="width:20px;"
- <?php if ($_['encryption_mode'] == 'server') echo "checked='checked'"; if ($_['encryption_mode'] != 'none') echo "DISABLED"?>
- />
-
- <?php echo $l->t('Server side encryption (allows you to access your files from the web interface and the desktop client)'); ?>
- <br />
-
- <input
- type="radio"
- name="encryption_mode"
- id="user_encryption"
- value="user"
- style="width:20px;"
- <?php if ($_['encryption_mode'] == 'user') echo "checked='checked'"; if ($_['encryption_mode'] != 'none') echo "DISABLED"?>
- />
-
- <?php echo $l->t('User specific (let the user decide)'); ?>
- <br/>
-
- <input
- type="radio"
- name="encryption_mode"
- id="none_encryption"
- value="none"
- style="width:20px;"
- <?php if ($_['encryption_mode'] == 'none') echo "checked='checked'"; if ($_['encryption_mode'] != 'none') echo "DISABLED"?>
- />
-
- <?php echo $l->t('None (no encryption at all)'); ?>
- <br/>
-
- </p>
<p>
<strong><?php echo $l->t('Encryption'); ?></strong>
- <?php echo $l->t("Exclude the following file types from encryption"); ?>
+ <?php echo $l->t("Exclude the following file types from encryption:"); ?>
+ <br />
<select
id='encryption_blacklist'
function testIsEncryptedContent() {
- $this->assertFalse( Encryption\Crypt::isEncryptedContent( $this->dataUrl ) );
+ $this->assertFalse( Encryption\Crypt::isCatfile( $this->dataUrl ) );
- $this->assertFalse( Encryption\Crypt::isEncryptedContent( $this->legacyEncryptedData ) );
+ $this->assertFalse( Encryption\Crypt::isCatfile( $this->legacyEncryptedData ) );
$keyfileContent = Encryption\Crypt::symmetricEncryptFileContent( $this->dataUrl, 'hat' );
- $this->assertTrue( Encryption\Crypt::isEncryptedContent( $keyfileContent ) );
+ $this->assertTrue( Encryption\Crypt::isCatfile( $keyfileContent ) );
}
$this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key
$this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key
- $this->view = new OC_FilesystemView( '/admin' );
+ $this->view = new \OC_FilesystemView( '/' );
$this->mockView = m::mock('OC_FilesystemView');
$this->util = new Encryption\Util( $this->mockView, $this->userId );
$mockView = m::mock('OC_FilesystemView');
- $mockView->shouldReceive( 'file_exists' )->times(4)->andReturn( false );
- $mockView->shouldReceive( 'mkdir' )->times(3)->andReturn( true );
+ $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false );
+ $mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId );
$mockView = m::mock('OC_FilesystemView');
- $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( true );
+ $mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true );
$mockView->shouldReceive( 'file_put_contents' )->withAnyArgs();
$util = new Encryption\Util( $mockView, $this->userId );
# then false will be returned. Use strict ordering?
}
+
+ function testFindFiles() {
+
+// $this->view->chroot( "/data/{$this->userId}/files" );
+
+ $util = new Encryption\Util( $this->view, $this->userId );
+
+ $files = $util->findFiles( '/', 'encrypted' );
+
+ var_dump( $files );
+
+ # TODO: Add more tests here to check that if any of the dirs are
+ # then false will be returned. Use strict ordering?
+
+ }
// /**
// * @brief test decryption using legacy blowfish method