2015-04-09 19:22:09 +02:00
# pragma once
# ifndef BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_
# define BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_
# include "../../interface/Block.h"
2015-04-09 21:17:28 +02:00
# include "../../utils/Data.h"
2015-04-18 14:47:12 +02:00
# include "../../interface/BlockStore.h"
2015-04-09 19:22:09 +02:00
# include "messmer/cpp-utils/macros.h"
# include <memory>
2015-04-24 21:08:36 +02:00
# include <boost/optional.hpp>
2015-04-09 19:22:09 +02:00
namespace blockstore {
namespace encrypted {
2015-04-24 18:14:25 +02:00
template < class Cipher > class EncryptedBlockStore ;
2015-04-09 19:22:09 +02:00
2015-04-24 18:14:25 +02:00
//TODO not only encryption, but also hmac
template < class Cipher >
2015-04-09 19:22:09 +02:00
class EncryptedBlock : public Block {
public :
2015-04-24 18:14:25 +02:00
static std : : unique_ptr < EncryptedBlock > TryCreateNew ( BlockStore * baseBlockStore , const Key & key , Data data , const typename Cipher : : EncryptionKey & encKey ) ;
2015-04-24 21:08:36 +02:00
static std : : unique_ptr < EncryptedBlock > TryDecrypt ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & key ) ;
2015-04-24 18:14:25 +02:00
2015-04-09 19:22:09 +02:00
//TODO Storing key twice (in parent class and in object pointed to). Once would be enough.
2015-04-24 18:14:25 +02:00
EncryptedBlock ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & key , Data plaintextData ) ;
2015-04-09 21:17:28 +02:00
virtual ~ EncryptedBlock ( ) ;
2015-04-09 19:22:09 +02:00
const void * data ( ) const override ;
void write ( const void * source , uint64_t offset , uint64_t size ) override ;
void flush ( ) override ;
size_t size ( ) const override ;
2015-04-24 18:14:25 +02:00
std : : unique_ptr < Block > releaseBlock ( ) ;
2015-04-09 21:17:28 +02:00
2015-04-09 19:22:09 +02:00
private :
std : : unique_ptr < Block > _baseBlock ;
2015-04-09 21:17:28 +02:00
Data _plaintextData ;
2015-04-24 18:14:25 +02:00
typename Cipher : : EncryptionKey _encKey ;
2015-04-09 21:17:28 +02:00
bool _dataChanged ;
void _encryptToBaseBlock ( ) ;
2015-04-09 19:22:09 +02:00
DISALLOW_COPY_AND_ASSIGN ( EncryptedBlock ) ;
} ;
2015-04-24 18:14:25 +02:00
template < class Cipher >
std : : unique_ptr < EncryptedBlock < Cipher > > EncryptedBlock < Cipher > : : TryCreateNew ( BlockStore * baseBlockStore , const Key & key , Data data , const typename Cipher : : EncryptionKey & encKey ) {
2015-04-24 21:08:36 +02:00
Data encrypted = Cipher : : encrypt ( ( byte * ) data . data ( ) , data . size ( ) , encKey ) ;
2015-04-24 18:14:25 +02:00
auto baseBlock = baseBlockStore - > tryCreate ( key , std : : move ( encrypted ) ) ;
if ( baseBlock . get ( ) = = nullptr ) {
//TODO Test this code branch
return nullptr ;
}
return std : : make_unique < EncryptedBlock > ( std : : move ( baseBlock ) , encKey , std : : move ( data ) ) ;
}
template < class Cipher >
2015-04-24 21:08:36 +02:00
std : : unique_ptr < EncryptedBlock < Cipher > > EncryptedBlock < Cipher > : : TryDecrypt ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & encKey ) {
//TODO Change BlockStore so we can read their "class Data" objects instead of "void *data()", and then we can change the Cipher interface to take Data objects instead of "byte *" + size
boost : : optional < Data > plaintext = Cipher : : decrypt ( ( byte * ) baseBlock - > data ( ) , baseBlock - > size ( ) , encKey ) ;
if ( ! plaintext ) {
return nullptr ;
}
return std : : make_unique < EncryptedBlock < Cipher > > ( std : : move ( baseBlock ) , encKey , std : : move ( * plaintext ) ) ;
2015-04-24 18:14:25 +02:00
}
template < class Cipher >
EncryptedBlock < Cipher > : : EncryptedBlock ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & encKey , Data plaintextData )
: Block ( baseBlock - > key ( ) ) ,
_baseBlock ( std : : move ( baseBlock ) ) ,
_plaintextData ( std : : move ( plaintextData ) ) ,
_encKey ( encKey ) ,
_dataChanged ( false ) {
}
template < class Cipher >
EncryptedBlock < Cipher > : : ~ EncryptedBlock ( ) {
_encryptToBaseBlock ( ) ;
}
template < class Cipher >
const void * EncryptedBlock < Cipher > : : data ( ) const {
return _plaintextData . data ( ) ;
}
template < class Cipher >
void EncryptedBlock < Cipher > : : write ( const void * source , uint64_t offset , uint64_t size ) {
assert ( offset < = _plaintextData . size ( ) & & offset + size < = _plaintextData . size ( ) ) ; //Also check offset < _data->size() because of possible overflow in the addition
std : : memcpy ( ( uint8_t * ) _plaintextData . data ( ) + offset , source , size ) ;
_dataChanged = true ;
}
template < class Cipher >
void EncryptedBlock < Cipher > : : flush ( ) {
_encryptToBaseBlock ( ) ;
return _baseBlock - > flush ( ) ;
}
template < class Cipher >
size_t EncryptedBlock < Cipher > : : size ( ) const {
return _plaintextData . size ( ) ;
}
template < class Cipher >
void EncryptedBlock < Cipher > : : _encryptToBaseBlock ( ) {
if ( _dataChanged ) {
2015-04-24 21:08:36 +02:00
Data encrypted = Cipher : : encrypt ( ( byte * ) _plaintextData . data ( ) , _plaintextData . size ( ) , _encKey ) ;
2015-04-24 18:14:25 +02:00
_baseBlock - > write ( encrypted . data ( ) , 0 , encrypted . size ( ) ) ;
_dataChanged = false ;
}
}
template < class Cipher >
std : : unique_ptr < Block > EncryptedBlock < Cipher > : : releaseBlock ( ) {
return std : : move ( _baseBlock ) ;
}
2015-04-09 19:22:09 +02:00
}
}
# endif