Skip to content
Snippets Groups Projects
Commit 92162898 authored by Sam Tuke's avatar Sam Tuke
Browse files

Wrote new methods for testing if a file is encrypted using AES or Blowfish

Added more unit tests for crypt class
Added new method for generating 16 character pseudo-random initialisation vectors
Started writing new methods for handling legacy keys and en/de/re cryption
Added comments to lib/filecache.php explaining expected $path type
parent e32b2c81
No related branches found
No related tags found
No related merge requests found
...@@ -50,6 +50,66 @@ class Crypt { ...@@ -50,6 +50,66 @@ class Crypt {
} }
/**
* @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 * @brief Symmetrically encrypt a string
* @returns encrypted file * @returns encrypted file
...@@ -106,13 +166,12 @@ class Crypt { ...@@ -106,13 +166,12 @@ class Crypt {
} }
$random = openssl_random_pseudo_bytes( 13 ); $iv = self::generateIv();
$iv = substr( base64_encode( $random ), 0, -4 );
if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) {
$combinedKeyfile = $encryptedContent .= $iv; // Combine content to encrypt with IV identifier and actual IV
$combinedKeyfile = $encryptedContent . '00iv00' . $iv;
return $combinedKeyfile; return $combinedKeyfile;
...@@ -143,9 +202,11 @@ class Crypt { ...@@ -143,9 +202,11 @@ class Crypt {
} }
// Fetch IV from end of file
$iv = substr( $keyfileContent, -16 ); $iv = substr( $keyfileContent, -16 );
$encryptedContent = substr( $keyfileContent, 0, -16 ); // Remove IV and IV identifier text to expose encrypted content
$encryptedContent = substr( $keyfileContent, 0, -22 );
if ( $plainContent = self::decrypt( $encryptedContent, $iv, $passphrase ) ) { if ( $plainContent = self::decrypt( $encryptedContent, $iv, $passphrase ) ) {
...@@ -269,6 +330,33 @@ class Crypt { ...@@ -269,6 +330,33 @@ class Crypt {
} }
/**
* @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 * @brief Generate a pseudo random 1024kb ASCII key
* @returns $key Generated key * @returns $key Generated key
......
...@@ -37,6 +37,23 @@ namespace OCA_Encryption; ...@@ -37,6 +37,23 @@ namespace OCA_Encryption;
class Util { class Util {
# DONE: add method to check if file is encrypted using new system
# DONE: add method to check if file is encrypted using old system
# 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
# TODO: test new encryption with proxies
private $view; // OC_FilesystemView object for filesystem operations private $view; // OC_FilesystemView object for filesystem operations
private $pwd; // User Password private $pwd; // User Password
private $client; // Client side encryption mode flag private $client; // Client side encryption mode flag
...@@ -73,6 +90,10 @@ class Util { ...@@ -73,6 +90,10 @@ class Util {
} }
/**
* @brief Sets up encryption folders and keys for a user
* @param $passphrase passphrase to encrypt server-stored private key with
*/
public function setup( $passphrase = null ) { public function setup( $passphrase = null ) {
$publicKeyFileName = 'encryption.public.key'; $publicKeyFileName = 'encryption.public.key';
...@@ -130,4 +151,101 @@ class Util { ...@@ -130,4 +151,101 @@ class Util {
} }
/**
* @brief Fetch the legacy encryption key from user files
* @param string $login used to locate the legacy key
* @param string $passphrase used to decrypt the legacy key
* @return true / false
*
* if the key is left out, the default handeler will be used
*/
public function getLegacyKey( $login, $passphrase ) {
OC_FileProxy::$enabled = false;
if (
$login
and $passphrase
and $key = $this->view->file_get_contents( '/' . $login . '/encryption.key' )
) {
OC_FileProxy::$enabled = true;
return $this->legacyDecrypt( $key, $passphrase );
} else {
OC_FileProxy::$enabled = true;
return false;
}
}
/**
* @brief Get the blowfish encryption handeler for a key
* @param $key string (optional)
* @return Crypt_Blowfish blowfish object
*
* if the key is left out, the default handeler will be used
*/
public function getBlowfish( $key = '' ) {
if( $key ){
return new Crypt_Blowfish($key);
} else {
return false;
}
}
/**
* @brief encrypts content using legacy blowfish system
* @param $content the cleartext message you want to encrypt
* @param $key the encryption key (optional)
* @returns encrypted content
*
* This function encrypts an content
*/
public static function legacyEncrypt( $content, $key='') {
$bf = self::getBlowfish($key);
return $bf->encrypt($content);
}
/**
* @brief decryption of an content
* @param $content the cleartext message you want to decrypt
* @param $key the encryption key (optional)
* @returns cleartext content
*
* This function decrypts an content
*/
public static function legacyDecrypt( $content, $key = '' ) {
$bf = $this->getBlowfish( $key );
$data = $bf->decrypt( $content );
return $data;
}
/**
* @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV
* @param $legacyContent the legacy encrypted content to re-encrypt
* @returns cleartext content
*
* This function decrypts an content
*/
public function legacyRecrypt( $legacyContent ) {
# TODO: write me
}
} }
<?php <?php
/** /**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and
* Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or * This file is licensed under the Affero General Public License version 3 or
* later. * later.
* See the COPYING-README file. * See the COPYING-README file.
*/ */
require realpath( dirname(__FILE__).'/../lib/crypt.php' ); require realpath( dirname(__FILE__).'/../lib/crypt.php' );
//require realpath( dirname(__FILE__).'/../../../lib/filecache.php' );
class Test_Encryption extends UnitTestCase { class Test_Encryption extends UnitTestCase {
...@@ -14,6 +16,7 @@ class Test_Encryption extends UnitTestCase { ...@@ -14,6 +16,7 @@ class Test_Encryption extends UnitTestCase {
// set content for encrypting / decrypting in tests // set content for encrypting / decrypting in tests
$this->data = realpath( dirname(__FILE__).'/../lib/crypt.php' ); $this->data = realpath( dirname(__FILE__).'/../lib/crypt.php' );
$this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' );
} }
...@@ -25,10 +28,22 @@ class Test_Encryption extends UnitTestCase { ...@@ -25,10 +28,22 @@ class Test_Encryption extends UnitTestCase {
$key = OCA_Encryption\Crypt::generateKey(); $key = OCA_Encryption\Crypt::generateKey();
$this->assertTrue( $key );
$this->assertTrue( strlen( $key ) > 1000 ); $this->assertTrue( strlen( $key ) > 1000 );
} }
function testGenerateIv() {
$iv = OCA_Encryption\Crypt::generateIv();
$this->assertTrue( $iv );
$this->assertTrue( strlen( $iv ) == 16 );
}
function testEncrypt() { function testEncrypt() {
$random = openssl_random_pseudo_bytes( 13 ); $random = openssl_random_pseudo_bytes( 13 );
...@@ -85,6 +100,31 @@ class Test_Encryption extends UnitTestCase { ...@@ -85,6 +100,31 @@ class Test_Encryption extends UnitTestCase {
} }
function testIsEncryptedContent() {
$this->assertFalse( OCA_Encryption\Crypt::isEncryptedContent( $this->data ) );
$this->assertFalse( OCA_Encryption\Crypt::isEncryptedContent( $this->legacyEncryptedData ) );
$keyfileContent = OCA_Encryption\Crypt::symmetricEncryptFileContent( $this->data, 'hat' );
$this->assertTrue( OCA_Encryption\Crypt::isEncryptedContent( $keyfileContent ) );
}
// // Cannot use this test for now due to hidden dependencies in OC_FileCache
// function testIsLegacyEncryptedContent() {
//
// $keyfileContent = OCA_Encryption\Crypt::symmetricEncryptFileContent( $this->legacyEncryptedData, 'hat' );
//
// $this->assertFalse( OCA_Encryption\Crypt::isLegacyEncryptedContent( $keyfileContent, '/files/admin/test.txt' ) );
//
// OC_FileCache::put( '/admin/files/legacy-encrypted-test.txt', $this->legacyEncryptedData );
//
// $this->assertTrue( OCA_Encryption\Crypt::isLegacyEncryptedContent( $this->legacyEncryptedData, '/files/admin/test.txt' ) );
//
// }
function testMultiKeyEncrypt() { function testMultiKeyEncrypt() {
# TODO: search in keyfile for actual content as IV will ensure this test always passes # TODO: search in keyfile for actual content as IV will ensure this test always passes
......
File added
...@@ -23,10 +23,16 @@ ...@@ -23,10 +23,16 @@
* provide caching for filesystem info in the database * provide caching for filesystem info in the database
* *
* not used by OC_Filesystem for reading filesystem info, * not used by OC_Filesystem for reading filesystem info,
* instread apps should use OC_FileCache::get where possible * instead apps should use OC_FileCache::get where possible
*
* It will try to keep the data up to date but changes from outside
* ownCloud can invalidate the cache
*
* Methods that take $path and $root params expect $path to be relative, like
* /admin/files/file.txt, if $root is false
* *
* It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache
*/ */
class OC_FileCache{ class OC_FileCache{
/** /**
* get the filesystem info from the cache * get the filesystem info from the cache
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment