2017-09-23 21:17:05 +02:00
# include "LocalStateMetadata.h"
# include <boost/property_tree/ptree.hpp>
# include <boost/property_tree/json_parser.hpp>
# include <cpp-utils/random/Random.h>
# include <boost/filesystem.hpp>
# include <blockstore/implementations/integrity/KnownBlockVersions.h>
2019-01-26 08:38:34 +01:00
# include <cryfs/impl/CryfsException.h>
2017-09-23 21:17:05 +02:00
using boost : : optional ;
using boost : : none ;
using boost : : property_tree : : ptree ;
using boost : : property_tree : : write_json ;
using boost : : property_tree : : read_json ;
using std : : ifstream ;
using std : : ofstream ;
using std : : istream ;
using std : : ostream ;
2017-09-30 09:49:24 +02:00
using std : : string ;
2017-09-23 21:17:05 +02:00
using blockstore : : integrity : : KnownBlockVersions ;
2017-09-30 09:49:24 +02:00
using cpputils : : hash : : Hash ;
using cpputils : : Data ;
using cpputils : : Random ;
2017-09-23 21:17:05 +02:00
namespace bf = boost : : filesystem ;
2019-05-25 21:27:31 +02:00
using namespace cpputils : : logging ;
2017-09-23 21:17:05 +02:00
namespace cryfs {
2017-09-30 09:49:24 +02:00
LocalStateMetadata : : LocalStateMetadata ( uint32_t myClientId , Hash encryptionKeyHash )
2017-10-29 17:35:10 +01:00
: _myClientId ( myClientId ) , _encryptionKeyHash ( encryptionKeyHash ) { }
2017-09-23 21:17:05 +02:00
2018-02-03 17:33:59 +01:00
LocalStateMetadata LocalStateMetadata : : loadOrGenerate ( const bf : : path & statePath , const Data & encryptionKey , bool allowReplacedFilesystem ) {
2017-09-23 21:17:05 +02:00
auto metadataFile = statePath / " metadata " ;
2021-05-08 23:44:27 +02:00
auto loaded = load_ ( metadataFile ) ;
2017-09-30 09:49:24 +02:00
if ( loaded = = none ) {
// If it couldn't be loaded, generate a new client id.
2021-05-08 23:44:27 +02:00
return generate_ ( metadataFile , encryptionKey ) ;
2017-09-23 21:17:05 +02:00
}
2017-09-30 09:49:24 +02:00
2018-02-03 17:33:59 +01:00
if ( ! allowReplacedFilesystem & & loaded - > _encryptionKeyHash . digest ! = cpputils : : hash : : hash ( encryptionKey , loaded - > _encryptionKeyHash . salt ) . digest ) {
2018-02-02 03:09:28 +01:00
throw CryfsException ( " The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system? " , ErrorCode : : EncryptionKeyChanged ) ;
2017-09-30 09:49:24 +02:00
}
return * loaded ;
2017-09-23 21:17:05 +02:00
}
2021-05-08 23:44:27 +02:00
optional < LocalStateMetadata > LocalStateMetadata : : load_ ( const bf : : path & metadataFilePath ) {
2018-05-21 01:20:38 +02:00
ifstream file ( metadataFilePath . string ( ) ) ;
2017-09-23 21:17:05 +02:00
if ( ! file . good ( ) ) {
// State file doesn't exist
return none ;
}
2021-05-08 23:44:27 +02:00
return deserialize_ ( file ) ;
2017-09-23 21:17:05 +02:00
}
2021-05-08 23:44:27 +02:00
void LocalStateMetadata : : save_ ( const bf : : path & metadataFilePath ) const {
2018-05-21 01:20:38 +02:00
ofstream file ( metadataFilePath . string ( ) , std : : ios : : trunc ) ;
2021-05-08 23:44:27 +02:00
serialize_ ( file ) ;
2017-09-23 21:17:05 +02:00
}
namespace {
2021-05-08 23:44:27 +02:00
uint32_t generateClientId_ ( ) {
2020-08-01 03:08:19 +02:00
uint32_t result = 0 ;
2017-09-23 21:17:05 +02:00
do {
2017-12-03 20:01:41 +01:00
result = cpputils : : deserialize < uint32_t > ( Random : : PseudoRandom ( ) . getFixedSize < sizeof ( uint32_t ) > ( ) . data ( ) ) ;
2017-09-23 21:17:05 +02:00
} while ( result = = KnownBlockVersions : : CLIENT_ID_FOR_DELETED_BLOCK ) ; // Safety check - CLIENT_ID_FOR_DELETED_BLOCK shouldn't be used by any valid client.
return result ;
}
# ifndef CRYFS_NO_COMPATIBILITY
optional < uint32_t > _tryLoadClientIdFromLegacyFile ( const bf : : path & metadataFilePath ) {
auto myClientIdFile = metadataFilePath . parent_path ( ) / " myClientId " ;
2018-05-21 01:20:38 +02:00
ifstream file ( myClientIdFile . string ( ) ) ;
2017-09-23 21:17:05 +02:00
if ( ! file . good ( ) ) {
return none ;
}
2020-08-01 03:08:19 +02:00
uint32_t value = 0 ;
2017-09-23 21:17:05 +02:00
file > > value ;
file . close ( ) ;
2017-09-28 08:41:08 +02:00
bf : : remove ( myClientIdFile ) ;
2017-09-23 21:17:05 +02:00
return value ;
}
# endif
}
2021-05-08 23:44:27 +02:00
LocalStateMetadata LocalStateMetadata : : generate_ ( const bf : : path & metadataFilePath , const Data & encryptionKey ) {
uint32_t myClientId = generateClientId_ ( ) ;
2017-09-23 21:17:05 +02:00
# ifndef CRYFS_NO_COMPATIBILITY
// In the old format, this was stored in a "myClientId" file. If that file exists, load it from there.
optional < uint32_t > legacy = _tryLoadClientIdFromLegacyFile ( metadataFilePath ) ;
if ( legacy ! = none ) {
myClientId = * legacy ;
}
# endif
2017-09-30 09:49:24 +02:00
LocalStateMetadata result ( myClientId , cpputils : : hash : : hash ( encryptionKey , cpputils : : hash : : generateSalt ( ) ) ) ;
2021-05-08 23:44:27 +02:00
result . save_ ( metadataFilePath ) ;
2017-09-23 21:17:05 +02:00
return result ;
}
2021-05-08 23:44:27 +02:00
void LocalStateMetadata : : serialize_ ( ostream & stream ) const {
2017-09-23 21:17:05 +02:00
ptree pt ;
pt . put < uint32_t > ( " myClientId " , myClientId ( ) ) ;
2017-09-30 09:49:24 +02:00
pt . put < string > ( " encryptionKey.salt " , _encryptionKeyHash . salt . ToString ( ) ) ;
pt . put < string > ( " encryptionKey.hash " , _encryptionKeyHash . digest . ToString ( ) ) ;
2017-09-23 21:17:05 +02:00
write_json ( stream , pt ) ;
}
2021-05-08 23:44:27 +02:00
LocalStateMetadata LocalStateMetadata : : deserialize_ ( istream & stream ) {
2019-05-25 21:27:31 +02:00
try {
ptree pt ;
read_json ( stream , pt ) ;
uint32_t myClientId = pt . get < uint32_t > ( " myClientId " ) ;
string encryptionKeySalt = pt . get < string > ( " encryptionKey.salt " ) ;
string encryptionKeyDigest = pt . get < string > ( " encryptionKey.hash " ) ;
return LocalStateMetadata ( myClientId , Hash {
/*.digest = */ cpputils : : hash : : Digest : : FromString ( encryptionKeyDigest ) ,
/*.salt = */ cpputils : : hash : : Salt : : FromString ( encryptionKeySalt )
} ) ;
}
catch ( . . . ) {
LOG ( ERR , " Error loading LocalStateMetadata " ) ;
throw ;
}
2017-09-23 21:17:05 +02:00
}
}