Working on stream{} writing

Development snapshot
remotes/origin/stable6
Sam Tuke 12 years ago
parent 770dcbf663
commit f89a3604aa
  1. 4
      apps/files_encryption/appinfo/spec.txt
  2. 22
      apps/files_encryption/lib/keymanager.php
  3. 44
      apps/files_encryption/lib/proxy.php
  4. 73
      apps/files_encryption/lib/stream.php
  5. 39
      apps/files_encryption/lib/util.php

@ -71,3 +71,7 @@ Notes
their files are scanned to detect unencrypted and legacy encrypted files, and
they are (re)encrypted as necessary. This may present a performance issue; we
need to monitor this.
- When files are saved to ownCloud via WebDAV, a .part file extension is used so
that the file isn't cached before the upload has been completed. .part files
are not compatible with files_encrytion's key management system however, so
we have to always sanitise such paths manually before using them.

@ -132,6 +132,28 @@ class Keymanager {
}
/**
* @brief Remove .path extension from a file path
* @param string $path Path that may identify a .part file
* @return string File path without .part extension
*/
public static function fixPartialFilePath( $path ) {
if ( preg_match( '/\.part$/', $path ) ) {
$newLength = strlen( $path ) - 5;
$fPath = substr( $path, 0, $newLength );
return $fPath;
} else {
return $path;
}
}
/**
* @brief retrieve keyfile for an encrypted file
* @param \OC_FilesystemView $view

@ -138,34 +138,9 @@ class Proxy extends \OC_FileProxy {
// Encrypt data
$encData = Crypt::symmetricEncryptFileContent( $data, $plainKey );
// Check if key recovery is enabled
$recoveryEnabled = $util->recoveryEnabled();
$sharingEnabled = \OCP\Share::isEnabled();
// Make sure that a share key is generated for the owner too
$userIds = array( $userId );
if ( \OCP\Share::isEnabled() ) {
// Find out who, if anyone, is sharing the file
$shareUids = \OCP\Share::getUsersSharingFile( $filePath, true, true, true );
$userIds = array_merge( $userIds, $shareUids );
}
// If recovery is enabled, add the
// Admin UID to list of users to share to
if ( $recoveryEnabled ) {
// FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB?
$adminUid = 'recoveryAdmin';
$userIds[] = $adminUid;
}
// Remove duplicate UIDs
$uniqueUserIds = array_unique ( $userIds );
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath );
// Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $rootView, $uniqueUserIds );
@ -280,6 +255,8 @@ class Proxy extends \OC_FileProxy {
*/
public function preUnlink( $path ) {
$path = Keymanager::fixPartialFilePath( $path );
// Disable encryption proxy to prevent recursive calls
\OC_FileProxy::$enabled = false;
@ -290,17 +267,20 @@ class Proxy extends \OC_FileProxy {
$util = new Util( $view, $userId );
// 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);
$fileOwner = \OC\Files\Filesystem::getOwner( $path );
$ownerPath = $util->stripUserFilesPath( $path ); // TODO: Don't trust $path, fetch owner path
$filePath = $owner . '/' . 'files_encryption' . '/' . 'keyfiles' . '/'. $ownerPath;
$filePath = $fileOwner . '/' . 'files_encryption' . '/' . 'keyfiles' . '/'. $ownerPath;
// Delete keyfile & shareKey so it isn't orphaned
if (
! (
Keymanager::deleteFileKey( $view, $owner, $ownerPath )
&& Keymanager::delShareKey( $view, $owner, $ownerPath )
Keymanager::deleteFileKey( $view, $fileOwner, $ownerPath )
&& Keymanager::delShareKey( $view, $fileOwner, $ownerPath )
)
) {

@ -44,6 +44,9 @@ namespace OCA\Encryption;
* buffer size used internally by PHP. The encryption process makes the input
* data longer, and input is chunked into smaller pieces in order to result in
* a 8192 encrypted block size.
* @note When files are deleted via webdav, or when they are updated and the
* previous version deleted, this is handled by OC\Files\View, and thus the
* encryption proxies are used and keyfiles deleted.
*/
class Stream {
@ -172,15 +175,19 @@ class Stream {
// Get the data from the file handle
$data = fread( $this->handle, 8192 );
$result = '';
if ( strlen( $data ) ) {
$this->getKey();
if ( ! $this->getKey() ) {
$result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile );
// 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' );
} else {
}
$result = '';
// Decrypt data
$result = Crypt::symmetricDecryptFileContent( $data, $this->plainKey );
}
@ -224,18 +231,20 @@ class Stream {
*/
public function getKey() {
// fix performance issues
if(isset($this->keyfile) && isset($this->encKeyfile)) {
return true;
}
// Check if key is already set
if ( isset( $this->plainKey ) && isset( $this->encKeyfile ) ) {
// If a keyfile already exists for a file named identically to
// file to be written
if ( $this->rootView->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->relPath . '.key' ) ) {
return true;
}
// TODO: add error handling for when file exists but no
// keyfile
// Avoid problems with .part file extensions
$this->relPath = Keymanager::fixPartialFilePath( $this->relPath );
// If a keyfile already exists
if ( $this->rootView->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->relPath . '.key' ) ) {
// Fetch and decrypt keyfile
// Fetch existing keyfile
$this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->relPath );
@ -247,7 +256,12 @@ class Stream {
$shareKey = Keymanager::getShareKey( $this->rootView, $this->userId, $this->relPath );
$this->keyfile = Crypt::multiKeyDecrypt( $this->encKeyfile, $shareKey, $privateKey );
$this->plainKey = Crypt::multiKeyDecrypt( $this->encKeyfile, $shareKey, $privateKey );
trigger_error( '$this->relPath = '.$this->relPath );
trigger_error( '$this->userId = '.$this->userId);
trigger_error( '$this->encKeyfile = '.$this->encKeyfile );
trigger_error( '$this->plainKey1 = '.var_export($this->plainKey, 1));
return true;
@ -303,7 +317,7 @@ class Stream {
$pointer = ftell( $this->handle );
// Make sure the userId is set
$this->getuser();
$this->setUserProperty();
// TODO: Check if file is shared, if so, use multiKeyEncrypt and
// save shareKeys in necessary user directories
@ -313,21 +327,34 @@ class Stream {
// one), save the newly generated keyfile
if ( ! $this->getKey() ) {
// TODO: Reuse the keyfile, it it exists, instead of making a new one
$this->keyfile = Crypt::generateKey();
$util = new Util( $this->rootView, $this->userId );
$this->plainKey = Crypt::generateKey();
$this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId );
$this->encKeyfile = Crypt::keyEncrypt( $this->keyfile, $this->publicKey );
$sharingEnabled = \OCP\Share::isEnabled();
$uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $this->relPath );
// Fetch public keys for all users who will share the file
$publicKeys = Keymanager::getPublicKeys( $this->rootView, $uniqueUserIds );
$this->encKeyfiles = Crypt::multiKeyEncrypt( $this->plainKey, $publicKeys );
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\User::getUser();
// Save the new encrypted file key
Keymanager::setFileKey( $view, $this->relPath, $userId, $this->encKeyfile );
Keymanager::setShareKeys( $view, $this->relPath, $this->encKeyfiles['keys'] );
// trigger_error( '$this->relPath = '.$this->relPath );
// trigger_error( '$this->userId = '.$this->userId);
// trigger_error( '$this->encKeyfile = '.var_export($this->encKeyfiles, 1) );
}
// trigger_error( '$this->plainKey2 = '.var_export($this->plainKey, 1));
// If extra data is left over from the last round, make sure it
// is integrated into the next 6126 / 8192 block
if ( $this->writeCache ) {
@ -355,7 +382,7 @@ class Stream {
//
// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR );
//
// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->keyfile );
// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->plainKey );
//
// $x = substr( $block, 0, $currentPos % 8192 );
//
@ -396,7 +423,7 @@ class Stream {
// Read the chunk from the start of $data
$chunk = substr( $data, 0, 6126 );
$encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile );
$encrypted = $this->preWriteEncrypt( $chunk, $this->plainKey );
// Write the data chunk to disk. This will be
// attended to the last data chunk if the file
@ -461,7 +488,7 @@ class Stream {
// Set keyfile property for file in question
$this->getKey();
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile );
$encrypted = $this->preWriteEncrypt( $this->writeCache, $this->plainKey );
fwrite( $this->handle, $encrypted );

@ -865,6 +865,45 @@ class Util {
}
/**
* @brief Find, sanitise and format users sharing a file
* @note This wraps other methods into a portable bundle
*/
public function getSharingUsersArray( $sharingEnabled, $filePath ) {
// Check if key recovery is enabled
$recoveryEnabled = $this->recoveryEnabled();
// Make sure that a share key is generated for the owner too
$userIds = array( $this->userId );
if ( $sharingEnabled ) {
// Find out who, if anyone, is sharing the file
$shareUids = \OCP\Share::getUsersSharingFile( $filePath, true, true, true );
$userIds = array_merge( $userIds, $shareUids );
}
// If recovery is enabled, add the
// Admin UID to list of users to share to
if ( $recoveryEnabled ) {
// FIXME: Create a separate admin user purely for recovery, and create method in util for fetching this id from DB?
$adminUid = 'recoveryAdmin';
$userIds[] = $adminUid;
}
// Remove duplicate UIDs
$uniqueUserIds = array_unique ( $userIds );
return $uniqueUserIds;
}
/**
* @brief get uid of the owners of the file and the path to the file
* @param $shareFilePath Path of the file to check

Loading…
Cancel
Save