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-25 02:48:41 +02:00
# include <messmer/cpp-utils/data/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-26 22:20:04 +02:00
# include <iostream>
2015-04-24 21:08:36 +02:00
# include <boost/optional.hpp>
2015-04-24 21:30:54 +02:00
# include "ciphers/Cipher.h"
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-05-06 16:16:18 +02:00
//TODO Test EncryptedBlock
2015-04-24 18:14:25 +02:00
template < class Cipher >
2015-04-09 19:22:09 +02:00
class EncryptedBlock : public Block {
public :
2015-04-24 21:30:54 +02:00
BOOST_CONCEPT_ASSERT ( ( CipherConcept < Cipher > ) ) ;
2015-04-25 02:48:41 +02:00
static std : : unique_ptr < EncryptedBlock > TryCreateNew ( BlockStore * baseBlockStore , const Key & key , cpputils : : 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-26 16:36:48 +02:00
EncryptedBlock ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & key , cpputils : : Data plaintextWithHeader ) ;
2015-04-09 21:17:28 +02:00
virtual ~ EncryptedBlock ( ) ;
2015-04-09 19:22:09 +02:00
const void * data ( ) const override ;
2015-04-26 16:36:48 +02:00
void write ( const void * source , uint64_t offset , uint64_t count ) override ;
2015-04-09 19:22:09 +02:00
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-26 16:36:48 +02:00
cpputils : : Data _plaintextWithHeader ;
2015-04-24 18:14:25 +02:00
typename Cipher : : EncryptionKey _encKey ;
2015-04-09 21:17:28 +02:00
bool _dataChanged ;
2015-04-26 16:36:48 +02:00
static constexpr unsigned int HEADER_LENGTH = Key : : BINARY_LENGTH ;
2015-04-09 21:17:28 +02:00
void _encryptToBaseBlock ( ) ;
2015-04-26 16:36:48 +02:00
static cpputils : : Data _prependKeyHeaderToData ( const Key & key , cpputils : : Data data ) ;
static bool _keyHeaderIsCorrect ( const Key & key , const cpputils : : Data & data ) ;
2015-04-09 19:22:09 +02:00
DISALLOW_COPY_AND_ASSIGN ( EncryptedBlock ) ;
} ;
2015-04-26 16:36:48 +02:00
template < class Cipher >
constexpr unsigned int EncryptedBlock < Cipher > : : HEADER_LENGTH ;
2015-04-24 18:14:25 +02:00
template < class Cipher >
2015-04-25 02:48:41 +02:00
std : : unique_ptr < EncryptedBlock < Cipher > > EncryptedBlock < Cipher > : : TryCreateNew ( BlockStore * baseBlockStore , const Key & key , cpputils : : Data data , const typename Cipher : : EncryptionKey & encKey ) {
2015-04-26 16:36:48 +02:00
cpputils : : Data plaintextWithHeader = _prependKeyHeaderToData ( key , std : : move ( data ) ) ;
cpputils : : Data encrypted = Cipher : : encrypt ( ( byte * ) plaintextWithHeader . data ( ) , plaintextWithHeader . 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 ;
}
2015-04-26 16:36:48 +02:00
return std : : make_unique < EncryptedBlock > ( std : : move ( baseBlock ) , encKey , std : : move ( plaintextWithHeader ) ) ;
2015-04-24 18:14:25 +02:00
}
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
2015-04-26 16:36:48 +02:00
boost : : optional < cpputils : : Data > plaintextWithHeader = Cipher : : decrypt ( ( byte * ) baseBlock - > data ( ) , baseBlock - > size ( ) , encKey ) ;
if ( ! plaintextWithHeader ) {
//Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext)
2015-04-26 16:45:00 +02:00
//TODO Think about logging
2015-04-26 22:20:04 +02:00
std : : cerr < < " Decrypting block " < < baseBlock - > key ( ) . ToString ( ) < < " failed. Was the block modified by an attacker? " < < std : : endl ;
2015-04-24 21:08:36 +02:00
return nullptr ;
}
2015-04-26 16:36:48 +02:00
if ( ! _keyHeaderIsCorrect ( baseBlock - > key ( ) , * plaintextWithHeader ) ) {
//The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block
2015-04-26 16:45:00 +02:00
//TODO Think about logging
2015-04-26 22:20:04 +02:00
std : : cerr < < " Decrypting block " < < baseBlock - > key ( ) . ToString ( ) < < " failed due to invalid block key. Was the block modified by an attacker? " < < std : : endl ;
2015-04-26 16:36:48 +02:00
return nullptr ;
}
return std : : make_unique < EncryptedBlock < Cipher > > ( std : : move ( baseBlock ) , encKey , std : : move ( * plaintextWithHeader ) ) ;
}
template < class Cipher >
cpputils : : Data EncryptedBlock < Cipher > : : _prependKeyHeaderToData ( const Key & key , cpputils : : Data data ) {
static_assert ( HEADER_LENGTH > = Key : : BINARY_LENGTH , " Key doesn't fit into the header " ) ;
cpputils : : Data result ( data . size ( ) + HEADER_LENGTH ) ;
std : : memcpy ( result . data ( ) , key . data ( ) , Key : : BINARY_LENGTH ) ;
std : : memcpy ( ( uint8_t * ) result . data ( ) + Key : : BINARY_LENGTH , data . data ( ) , data . size ( ) ) ;
return result ;
}
template < class Cipher >
bool EncryptedBlock < Cipher > : : _keyHeaderIsCorrect ( const Key & key , const cpputils : : Data & data ) {
return 0 = = std : : memcmp ( key . data ( ) , data . data ( ) , Key : : BINARY_LENGTH ) ;
2015-04-24 18:14:25 +02:00
}
template < class Cipher >
2015-04-26 16:36:48 +02:00
EncryptedBlock < Cipher > : : EncryptedBlock ( std : : unique_ptr < Block > baseBlock , const typename Cipher : : EncryptionKey & encKey , cpputils : : Data plaintextWithHeader )
2015-04-24 18:14:25 +02:00
: Block ( baseBlock - > key ( ) ) ,
_baseBlock ( std : : move ( baseBlock ) ) ,
2015-04-26 16:36:48 +02:00
_plaintextWithHeader ( std : : move ( plaintextWithHeader ) ) ,
2015-04-24 18:14:25 +02:00
_encKey ( encKey ) ,
_dataChanged ( false ) {
}
template < class Cipher >
EncryptedBlock < Cipher > : : ~ EncryptedBlock ( ) {
_encryptToBaseBlock ( ) ;
}
template < class Cipher >
const void * EncryptedBlock < Cipher > : : data ( ) const {
2015-04-26 16:36:48 +02:00
return ( uint8_t * ) _plaintextWithHeader . data ( ) + HEADER_LENGTH ;
2015-04-24 18:14:25 +02:00
}
template < class Cipher >
2015-04-26 16:36:48 +02:00
void EncryptedBlock < Cipher > : : write ( const void * source , uint64_t offset , uint64_t count ) {
assert ( offset < = size ( ) & & offset + count < = size ( ) ) ; //Also check offset < size() because of possible overflow in the addition
std : : memcpy ( ( uint8_t * ) _plaintextWithHeader . data ( ) + HEADER_LENGTH + offset , source , count ) ;
2015-04-24 18:14:25 +02:00
_dataChanged = true ;
}
template < class Cipher >
void EncryptedBlock < Cipher > : : flush ( ) {
_encryptToBaseBlock ( ) ;
return _baseBlock - > flush ( ) ;
}
template < class Cipher >
size_t EncryptedBlock < Cipher > : : size ( ) const {
2015-04-26 16:36:48 +02:00
return _plaintextWithHeader . size ( ) - HEADER_LENGTH ;
2015-04-24 18:14:25 +02:00
}
template < class Cipher >
void EncryptedBlock < Cipher > : : _encryptToBaseBlock ( ) {
if ( _dataChanged ) {
2015-04-26 16:36:48 +02:00
cpputils : : Data encrypted = Cipher : : encrypt ( ( byte * ) _plaintextWithHeader . data ( ) , _plaintextWithHeader . 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