2017-09-30 23:42:34 +02:00
# include "testutils/CliTest.h"
# include <cryfs/config/CryConfigFile.h>
2018-02-02 03:09:28 +01:00
# include <cryfs/ErrorCodes.h>
2018-10-22 04:31:08 +02:00
# include <cpp-utils/crypto/kdf/Scrypt.h>
# include <cpp-utils/data/DataFixture.h>
2018-12-11 06:20:18 +01:00
# include <cpp-utils/tempfile/TempDir.h>
# include <blockstore/implementations/caching/CachingBlockStore2.h>
# include <cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h>
2017-09-30 23:42:34 +02:00
using std : : vector ;
using std : : string ;
using cryfs : : CryConfig ;
using cryfs : : CryConfigFile ;
2018-02-02 03:09:28 +01:00
using cryfs : : ErrorCode ;
2018-10-22 04:31:08 +02:00
using cryfs : : CryKeyProvider ;
using cpputils : : Data ;
using cpputils : : EncryptionKey ;
using cpputils : : SCrypt ;
2018-12-11 06:20:18 +01:00
using cpputils : : TempDir ;
namespace bf = boost : : filesystem ;
namespace {
void writeFile ( const bf : : path & filename , const string & content ) {
std : : ofstream file ( filename . c_str ( ) , std : : ios : : trunc ) ;
file < < content ;
ASSERT ( file . good ( ) , " Failed writing file to file system " ) ;
}
bool readingFileIsSuccessful ( const bf : : path & filename ) {
std : : ifstream file ( filename . c_str ( ) ) ;
std : : string content ;
file > > content ; // just read a little bit so we have a file access
return file . good ( ) ;
}
void recursive_copy ( const bf : : path & src , const bf : : path & dst ) {
if ( bf : : exists ( dst ) ) {
throw std : : runtime_error ( dst . generic_string ( ) + " already exists " ) ;
}
if ( bf : : is_directory ( src ) ) {
bf : : create_directories ( dst ) ;
for ( auto & item : bf : : directory_iterator ( src ) ) {
recursive_copy ( item . path ( ) , dst / item . path ( ) . filename ( ) ) ;
}
} else if ( bf : : is_regular_file ( src ) ) {
bf : : copy_file ( src , dst ) ;
} else {
throw std : : runtime_error ( dst . generic_string ( ) + " neither dir nor file " ) ;
}
}
2018-10-22 04:31:08 +02:00
class FakeCryKeyProvider final : public CryKeyProvider {
2018-12-11 06:20:18 +01:00
EncryptionKey requestKeyForExistingFilesystem ( size_t keySize , const Data & kdfParameters ) override {
2018-10-22 04:31:08 +02:00
return SCrypt ( SCrypt : : TestSettings ) . deriveExistingKey ( keySize , " pass " , kdfParameters ) ;
}
KeyResult requestKeyForNewFilesystem ( size_t keySize ) override {
auto derived = SCrypt ( SCrypt : : TestSettings ) . deriveNewKey ( keySize , " pass " ) ;
return {
2018-12-11 06:20:18 +01:00
std : : move ( derived . key ) ,
std : : move ( derived . kdfParameters )
2018-10-22 04:31:08 +02:00
} ;
}
} ;
2017-09-30 23:42:34 +02:00
2018-12-11 06:20:18 +01:00
class CliTest_IntegrityCheck : public CliTest {
2017-09-30 23:42:34 +02:00
public :
void modifyFilesystemId ( ) {
2018-10-22 04:31:08 +02:00
FakeCryKeyProvider keyProvider ;
auto configFile = CryConfigFile : : load ( basedir / " cryfs.config " , & keyProvider ) . value ( ) ;
2017-09-30 23:42:34 +02:00
configFile . config ( ) - > SetFilesystemId ( CryConfig : : FilesystemID : : FromString ( " 0123456789ABCDEF0123456789ABCDEF " ) ) ;
configFile . save ( ) ;
}
void modifyFilesystemKey ( ) {
2018-10-22 04:31:08 +02:00
FakeCryKeyProvider keyProvider ;
auto configFile = CryConfigFile : : load ( basedir / " cryfs.config " , & keyProvider ) . value ( ) ;
2017-09-30 23:42:34 +02:00
configFile . config ( ) - > SetEncryptionKey ( " 0123456789ABCDEF0123456789ABCDEF " ) ;
configFile . save ( ) ;
}
} ;
TEST_F ( CliTest_IntegrityCheck , givenIncorrectFilesystemId_thenFails ) {
2018-12-11 06:20:18 +01:00
vector < string > args { basedir . string ( ) . c_str ( ) , mountdir . string ( ) . c_str ( ) , " --cipher " , " aes-256-gcm " , " -f " } ;
2017-09-30 23:42:34 +02:00
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS ( args , mountdir ) ;
modifyFilesystemId ( ) ;
EXPECT_RUN_ERROR (
args ,
2018-12-19 06:36:13 +01:00
" Error 20: The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir. " ,
2018-02-02 03:09:28 +01:00
ErrorCode : : FilesystemIdChanged
2017-09-30 23:42:34 +02:00
) ;
}
TEST_F ( CliTest_IntegrityCheck , givenIncorrectFilesystemKey_thenFails ) {
2018-12-11 06:20:18 +01:00
vector < string > args { basedir . string ( ) . c_str ( ) , mountdir . string ( ) . c_str ( ) , " --cipher " , " aes-256-gcm " , " -f " } ;
2017-09-30 23:42:34 +02:00
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS ( args , mountdir ) ;
modifyFilesystemKey ( ) ;
EXPECT_RUN_ERROR (
args ,
2018-12-19 06:36:13 +01:00
" Error 21: The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system? " ,
2018-02-02 03:09:28 +01:00
ErrorCode : : EncryptionKeyChanged
2017-09-30 23:42:34 +02:00
) ;
}
2018-12-11 06:20:18 +01:00
// TODO Also enable this
TEST_F ( CliTest_IntegrityCheck , givenFilesystemWithRolledBackBasedir_whenMounting_thenFails ) {
vector < string > args { basedir . string ( ) . c_str ( ) , mountdir . string ( ) . c_str ( ) , " --cipher " , " aes-256-gcm " , " -f " } ;
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS/EXPECT_RUN_ERROR can handle that
// create a filesystem with one file
EXPECT_RUN_SUCCESS ( args , mountdir , [ & ] {
writeFile ( mountdir / " myfile " , " hello world " ) ;
} ) ;
// backup the base directory
TempDir backup ;
recursive_copy ( basedir , backup . path ( ) / " basedir " ) ;
// modify the file system contents
EXPECT_RUN_SUCCESS ( args , mountdir , [ & ] {
writeFile ( mountdir / " myfile " , " hello world 2 " ) ;
} ) ;
// roll back base directory
bf : : remove_all ( basedir ) ;
recursive_copy ( backup . path ( ) / " basedir " , basedir ) ;
// error code is success because it unmounts normally
2018-12-19 06:40:03 +01:00
EXPECT_RUN_ERROR ( args , " Integrity violation detected. Unmounting. " , ErrorCode : : IntegrityViolation , [ & ] {
2018-12-11 06:20:18 +01:00
EXPECT_FALSE ( readingFileIsSuccessful ( mountdir / " myfile " ) ) ;
} ) ;
// Test it doesn't mount anymore now because it's marked with an integrity violation
EXPECT_RUN_ERROR ( args , " There was an integrity violation detected. Preventing any further access to the file system. " , ErrorCode : : IntegrityViolationOnPreviousRun ) ;
}
TEST_F ( CliTest_IntegrityCheck , whenRollingBackBasedirWhileMounted_thenUnmounts ) {
vector < string > args { basedir . string ( ) . c_str ( ) , mountdir . string ( ) . c_str ( ) , " --cipher " , " aes-256-gcm " , " -f " } ;
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS/EXPECT_RUN_ERROR can handle that
// create a filesystem with one file
EXPECT_RUN_SUCCESS ( args , mountdir , [ & ] {
writeFile ( mountdir / " myfile " , " hello world " ) ;
} ) ;
// backup the base directory
TempDir backup ;
recursive_copy ( basedir , backup . path ( ) / " basedir " ) ;
2018-12-19 06:40:03 +01:00
EXPECT_RUN_ERROR ( args , " Integrity violation detected. Unmounting. " , ErrorCode : : IntegrityViolation , [ & ] {
2018-12-11 06:20:18 +01:00
// modify the file system contents
writeFile ( mountdir / " myfile " , " hello world 2 " ) ;
ASSERT ( readingFileIsSuccessful ( mountdir / " myfile " ) , " " ) ; // just to make sure reading usually works
// wait for cache timeout (i.e. flush file system to disk)
constexpr auto cache_timeout = blockstore : : caching : : CachingBlockStore2 : : MAX_LIFETIME_SEC + cryfs : : cachingfsblobstore : : CachingFsBlobStore : : MAX_LIFETIME_SEC ;
boost : : this_thread : : sleep_for ( boost : : chrono : : seconds ( static_cast < int > ( std : : ceil ( cache_timeout * 3 ) ) ) ) ;
// roll back base directory
bf : : remove_all ( basedir ) ;
recursive_copy ( backup . path ( ) / " basedir " , basedir ) ;
// expect reading now fails
EXPECT_FALSE ( readingFileIsSuccessful ( mountdir / " myfile " ) ) ;
} ) ;
// Test it doesn't mount anymore now because it's marked with an integrity violation
EXPECT_RUN_ERROR ( args , " There was an integrity violation detected. Preventing any further access to the file system. " , ErrorCode : : IntegrityViolationOnPreviousRun ) ;
}
}