diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 5e1078c9e1b..06a34c8f4d6 100755 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -342,15 +342,10 @@ class Crypt { // Remove padding $noPadding = self::removePadding( $keyfileContent ); - // Fetch IV from end of file - $iv = substr( $noPadding, -16 ); + // Split into enc data and catfile + $catfile = self::splitIv( $noPadding ); - // Remove IV and IV identifier text to expose encrypted content - $encryptedContent = substr( $noPadding, 0, -22 ); - - //trigger_error( "\n\n\$noPadding = ".var_export($noPadding)."\n\n\$iv = ".var_export($iv )."\n\n\$encryptedContent = ".var_export($encryptedContent) ); - - if ( $plainContent = self::decrypt( $encryptedContent, $iv, $passphrase ) ) { + if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { return $plainContent; @@ -493,12 +488,12 @@ class Crypt { public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) { // Decrypt the keyfile with the user's private key - $decryptedKey = self::keyDecrypt( $keyfile, $privateKey ); + $decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey ); // trigger_error( "\$keyfile = ".var_export($keyfile, 1)); // Decrypt the catfile symmetrically using the decrypted keyfile - $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKey ); + $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile ); return $decryptedData; @@ -704,12 +699,10 @@ class Crypt { * This function decrypts an content */ public static function legacyDecrypt( $content, $passphrase = '' ) { - - $passphrase = ''; //trigger_error("OC2 dec \$content = $content \$key = ".strlen($passphrase) ); - $bf = self::getBlowfish( "67362885833455692562" ); + $bf = self::getBlowfish( $passphrase ); // trigger_error(var_export($bf, 1) ); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 85664734d7a..08e708f879b 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -157,11 +157,11 @@ class Proxy extends \OC_FileProxy { //$cached = \OC_FileCache_Cached::get( $path, '' ); - $keyFile = Keymanager::getFileKey( $filePath ); + $encryptedKeyfile = Keymanager::getFileKey( $filePath ); $session = new Session(); - $decrypted = Crypt::keyDecryptKeyfile( $data, $keyFile, $session->getPrivateKey( $split[1] ) ); + $decrypted = Crypt::keyDecryptKeyfile( $data, $encryptedKeyfile, $session->getPrivateKey( $split[1] ) ); } elseif ( Crypt::mode() == 'server' diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index ac5fadd4e03..42b9233f7bb 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -302,10 +302,6 @@ class Stream { */ public function stream_write( $data ) { - - -// file_put_contents('/home/samtuke/newtmp.txt', 'stream_write('.$data.')' ); - // Disable the file proxies so that encryption is not automatically attempted when the file is written to disk - we are handling that separately here and we don't want to get into an infinite loop \OC_FileProxy::$enabled = false; diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 77f8dffe00f..bd8d18140ae 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -71,12 +71,20 @@ class Util { # Admin UI: + ## DONE: changing user password also changes encryption passphrase + ## TODO: add support for optional recovery in case of lost passphrase / keys ## TODO: add admin optional required long passphrase for users ## TODO: add UI buttons for encrypt / decrypt everything ## TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc. + # Sharing: + + ## TODO: add support for encrypting to multiple public keys + ## TODO: add support for decrypting to multiple private keys + + # Integration testing: ## TODO: test new encryption with webdav diff --git a/apps/files_encryption/tests/crypt.php b/apps/files_encryption/tests/crypt.php index f72f15ca236..24c6cff2722 100755 --- a/apps/files_encryption/tests/crypt.php +++ b/apps/files_encryption/tests/crypt.php @@ -45,7 +45,7 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase { $this->userId = 'admin'; $this->pass = 'admin'; - + } function tearDown(){} @@ -64,8 +64,6 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase { $iv = Encryption\Crypt::generateIv(); - echo $iv; - $this->assertEquals( 16, strlen( $iv ) ); return $iv; @@ -223,84 +221,106 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase { // Get file contents without using any wrapper to get it's actual contents on disk $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); - //echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile"; // Check that the file was encrypted before being written to disk $this->assertNotEquals( $this->dataShort, $retreivedCryptedFile ); + // Get private key + $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->userId, $this->view ); - $key = Encryption\Keymanager::getFileKey( $filename ); + $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); - $manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $key ); + // Get keyfile + $encryptedKeyfile = Encryption\Keymanager::getFileKey( $filename ); + + $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); + + + // Manually decrypt + $manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile ); + + // Check that decrypted data matches $this->assertEquals( $this->dataShort, $manualDecrypt ); } -// /** -// * @brief Test that data that is written by the crypto stream wrapper -// * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read -// */ -// function testSymmetricStreamEncryptLongFileContent() { -// -// // Generate a a random filename -// $filename = 'tmp-'.time(); -// -// echo "\n\n\$filename = $filename\n\n"; -// -// // Save long data as encrypted file using stream wrapper -// $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong ); -// -// // Test that data was successfully written -// $this->assertTrue( is_int( $cryptedFile ) ); -// -// // Get file contents without using any wrapper to get it's actual contents on disk -// $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); -// -// // echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n"; -// -// // Check that the file was encrypted before being written to disk -// $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile ); -// -// // Manuallly split saved file into separate IVs and encrypted chunks -// $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE); -// -// //print_r($r); -// -// // Join IVs and their respective data chunks -// $e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11] );//.$r[11], $r[12].$r[13], $r[14] ); -// -// //print_r($e); -// -// // Manually fetch keyfile -// $keyfile = Encryption\Keymanager::getFileKey( $filename ); -// -// // Set var for reassembling decrypted content -// $decrypt = ''; -// -// // Manually decrypt chunk -// foreach ($e as $e) { -// -// // echo "\n\$encryptMe = $f"; -// -// $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $keyfile ); -// -// // Assemble decrypted chunks -// $decrypt .= $chunkDecrypt; -// -// //echo "\n\$chunkDecrypt = $chunkDecrypt"; -// -// } -// -// $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt ); -// -// // Teardown -// -// $this->view->unlink( $filename ); -// -// Encryption\Keymanager::deleteFileKey( $filename ); -// -// } + /** + * @brief Test that data that is written by the crypto stream wrapper + * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read + * @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual + * reassembly of its data + */ + function testSymmetricStreamEncryptLongFileContent() { + + // Generate a a random filename + $filename = 'tmp-'.time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong ); + + // Test that data was successfully written + $this->assertTrue( is_int( $cryptedFile ) ); + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); + +// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n"; + + // Check that the file was encrypted before being written to disk + $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile ); + + // Manuallly split saved file into separate IVs and encrypted chunks + $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE); + + //print_r($r); + + // Join IVs and their respective data chunks + $e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] ); + + //print_r($e); + + + // Get private key + $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->userId, $this->view ); + + $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); + + + // Get keyfile + $encryptedKeyfile = Encryption\Keymanager::getFileKey( $filename ); + + $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); + + + // Set var for reassembling decrypted content + $decrypt = ''; + + // Manually decrypt chunk + foreach ($e as $e) { + +// echo "\n\$e = $e"; + + $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile ); + + // Assemble decrypted chunks + $decrypt .= $chunkDecrypt; + +// echo "\n\$chunkDecrypt = $chunkDecrypt"; + + } + +// echo "\n\$decrypt = $decrypt"; + + $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt ); + + // Teardown + + $this->view->unlink( $filename ); + + Encryption\Keymanager::deleteFileKey( $filename ); + + } /** * @brief Test that data that is read by the crypto stream wrapper @@ -493,17 +513,17 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase { } -// /** -// * @brief test decryption using legacy blowfish method -// * @depends testLegacyEncryptShort -// */ -// function testLegacyDecryptShort( $crypted ) { -// -// $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); -// -// $this->assertEquals( $this->dataShort, $decrypted ); -// -// } + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptShort + */ + function testLegacyDecryptShort( $crypted ) { + + $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); + + $this->assertEquals( $this->dataShort, $decrypted ); + + } /** * @brief test encryption using legacy blowfish method @@ -521,17 +541,17 @@ class Test_Crypt extends \PHPUnit_Framework_TestCase { } -// /** -// * @brief test decryption using legacy blowfish method -// * @depends testLegacyEncryptLong -// */ -// function testLegacyDecryptLong( $crypted ) { -// -// $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); -// -// $this->assertEquals( $this->dataLong, $decrypted ); -// -// } + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptLong + */ + function testLegacyDecryptLong( $crypted ) { + + $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); + + $this->assertEquals( $this->dataLong, $decrypted ); + + } /** * @brief test generation of legacy encryption key