+Encrypted files
+---------------
+
+- Each encrypted file has at least two components: the encrypted data file
+ ('catfile'), and it's corresponding key file ('keyfile'). Shared files have an
+ additional key file ('share key'). The catfile contains the encrypted data
+ concatenated with delimiter text, followed by the initialisation vector ('IV'),
+ and padding. e.g.:
+
+ [encrypted data string][delimiter][IV][padding]
+ [anhAAjAmcGXqj1X9g==][00iv00][MSHU5N5gECP7aAg7][xx] (square braces added)
+
Notes
-----
* @note This method should never be called for users using client side encryption\r
*/\r
public static function login( $params ) {\r
- \r
- // TODO: use lots of dependency injection here\r
\r
$view = new \OC_FilesystemView( '/' );\r
\r
\r
// Encrypt existing user files:\r
// This serves to upgrade old versions of the encryption\r
- // app (see appinfo/spec.txt\r
- $this->encryptAll( $publicKey, $this->userFilesDir, $session->getLegacyKey(), $params['password'] );\r
+ // app (see appinfo/spec.txt)\r
+ if ( \r
+ $util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )\r
+ ) {\r
+ \r
+ \OC_Log::write( \r
+ 'Encryption library', 'Encryption of file belonging to "' . $params['uid'] . '" was started at login'\r
+ , \OC_Log::INFO \r
+ );\r
+ \r
+ }\r
\r
return true;\r
\r
}\r
\r
/**\r
- * @brief retrieve file encryption key\r
+ * @brief Delete a keyfile\r
*\r
- * @param string file name\r
- * @return string file key or false\r
+ * @param OC_FilesystemView $view\r
+ * @param string $userId username\r
+ * @param string $path path of the file the key belongs to\r
+ * @return bool Outcome of unlink operation\r
+ * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT\r
+ * /data/admin/files/mydoc.txt\r
*/\r
- public static function deleteFileKey( $path, $staticUserClass = 'OCP\User' ) {\r
+ public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) {\r
\r
- $keypath = ltrim( $path, '/' );\r
- $user = $staticUserClass::getUser();\r
-\r
- // update $keypath and $user if path point to a file shared by someone else\r
-// $query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );\r
-// \r
-// $result = $query->execute( array ('/'.$user.'/files/'.$keypath, $user));\r
-// \r
-// if ($row = $result->fetchRow()) {\r
-// \r
-// $keypath = $row['source'];\r
-// $keypath_parts = explode( '/', $keypath );\r
-// $user = $keypath_parts[1];\r
-// $keypath = str_replace( '/' . $user . '/files/', '', $keypath );\r
-// \r
-// }\r
+ $trimmed = ltrim( $path, '/' );\r
+ $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key';\r
\r
- $view = new \OC_FilesystemView( '/' . $user. '/files_encryption/keyfiles/' );\r
+ // Unlink doesn't tell us if file was deleted (not found returns\r
+ // true), so we perform our own test\r
+ if ( $view->file_exists( $keyPath ) ) {\r
\r
- return $view->unlink( $keypath . '.key' );\r
+ return $view->unlink( $keyPath );\r
+ \r
+ } else {\r
+ \r
+ \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR );\r
+ \r
+ return false;\r
+ \r
+ }\r
\r
}\r
\r
}
+ /**
+ * @brief When a file is deleted, remove its keyfile also
+ */
+ public function postUnlink( $path ) {
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ $view = new \OC_FilesystemView( '/' );
+
+ $userId = \OCP\USER::getUser();
+
+ // Format path to be relative to user files dir
+ $trimmed = ltrim( $path, '/' );
+ $split = explode( '/', $trimmed );
+ $sliced = array_slice( $split, 2 );
+ $relPath = implode( '/', $sliced );
+
+ // Delete keyfile so it isn't orphaned
+ $result = Keymanager::deleteFileKey( $view, $userId, $relPath );
+
+ \OC_FileProxy::$enabled = true;
+
+ return $result;
+
+ }
+
+ /**
+ * @brief When a file is renamed, rename its keyfile also
+ * @return bool Result of rename()
+ * @note This is pre rather than post because using post didn't work
+ */
+ public function preRename( $oldPath, $newPath ) {
+
+// trigger_error( "PATHS = ".var_export($oldPath, 1).' '.var_export($newPath, 1));
+
+ // Disable encryption proxy to prevent recursive calls
+ \OC_FileProxy::$enabled = false;
+
+ $view = new \OC_FilesystemView( '/' );
+
+ $userId = \OCP\USER::getUser();
+
+ // Format paths to be relative to user files dir
+ $oldTrimmed = ltrim( $oldPath, '/' );
+ $oldSplit = explode( '/', $oldTrimmed );
+ $oldSliced = array_slice( $oldSplit, 2 );
+ $oldRelPath = implode( '/', $oldSliced );
+ $oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath . '.key';
+
+ $newTrimmed = ltrim( $newPath, '/' );
+ $newSplit = explode( '/', $newTrimmed );
+ $newSliced = array_slice( $newSplit, 2 );
+ $newRelPath = implode( '/', $newSliced );
+ $newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath . '.key';
+
+// trigger_error("RENAMING = ".var_export($oldKeyfilePath, 1).' -> '.var_export($newKeyfilePath, 1));
+
+ // Rename keyfile so it isn't orphaned
+ $result = $view->rename( $oldKeyfilePath, $newKeyfilePath );
+
+ \OC_FileProxy::$enabled = true;
+
+ return $result;
+
+ }
+
public function postFopen( $path, &$result ){
if ( !$result ) {
* @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
+
+ * @note $directory needs to be a path relative to OC data dir. e.g.
+ * /admin/files NOT /backup OR /home/www/oc/data/admin/files
*/
public function findFiles( $directory ) {
return false;
} else {
-
+
return $found;
}
if ( $found = $this->findFiles( $dirPath ) ) {
+ // Disable proxy to prevent file being encrypted twice
+ \OC_FileProxy::$enabled = false;
+
// Encrypt unencrypted files
- foreach ( $found['plain'] as $plainFilePath ) {
-
+ foreach ( $found['plain'] as $plainFile ) {
+
// Fetch data from file
- $plainData = $this->view->file_get_contents( $plainFilePath );
+ $plainData = $this->view->file_get_contents( $plainFile['path'] );
// Encrypt data, generate catfile
$encrypted = Crypt::keyEncryptKeyfile( $plainData, $publicKey );
+ // Format path to be relative to user files dir
+ $trimmed = ltrim( $plainFile['path'], '/' );
+ $split = explode( '/', $trimmed );
+ $sliced = array_slice( $split, 2 );
+ $relPath = implode( '/', $sliced );
+
// Save catfile
- Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $encrypted['key'] );
+ Keymanager::setFileKey( $this->view, $relPath, $this->userId, $encrypted['key'] );
// Overwrite the existing file with the encrypted one
- $this->view->file_put_contents( $plainFilePath, $encrypted['data'] );
+ $this->view->file_put_contents( $plainFile['path'], $encrypted['data'] );
}
$recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKey, $newPassphrase );
// Save catfile
- Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $recrypted['key'] );
+ Keymanager::setFileKey( $this->view, $plainFile['path'], $this->userId, $recrypted['key'] );
// Overwrite the existing file with the encrypted one
- $this->view->file_put_contents( $plainFilePath, $recrypted['data'] );
+ $this->view->file_put_contents( $plainFile['path'], $recrypted['data'] );
}
}
+
+ \OC_FileProxy::$enabled = true;
+
+ // If files were found, return true
+ return true;
+ } else {
+
+ // If no files were found, return false
+ return false;
+
}
}