1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
#ifndef ENCRYPTED_FILE_HH_INCLUDED
#define ENCRYPTED_FILE_HH_INCLUDED
#include <google/protobuf/io/zero_copy_stream.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <exception>
#include <vector>
#include "adler32.hh"
#include "encryption.hh"
#include "encryption_key.hh"
#include "ex.hh"
#include "unbuffered_file.hh"
/// Google's ZeroCopyStream implementations which read and write files encrypted
/// with our encryption mechanism. They also calculate adler32 of all file
/// content and write/check it at the end.
/// Encryption-wise we implement AES-128 in CBC mode with PKCS#7 padding. We
/// don't use EVP for this currently - everyone is welcome to change this, and
/// to add support for arbitrary ciphers, key lengths and modes of operations as
/// well. When no encryption key is set, no encryption or padding is done, but
/// everything else works the same way otherwise
namespace EncryptedFile {
DEF_EX( Ex, "Encrypted file exception", std::exception )
DEF_EX( exFileCorrupted, "encrypted file data is currupted", Ex )
DEF_EX( exIncorrectFileSize, "size of the encrypted file is incorrect", exFileCorrupted )
DEF_EX( exReadFailed, "read failed", Ex ) // Only thrown by InputStream::read()
DEF_EX( exAdlerMismatch, "adler32 mismatch", Ex )
class InputStream: public google::protobuf::io::ZeroCopyInputStream
{
public:
/// Opens the input file. If EncryptionKey contains no key, the input won't be
/// decrypted and iv would be ignored
InputStream( char const * fileName, EncryptionKey const &, void const * iv );
virtual bool Next( void const ** data, int * size );
virtual void BackUp( int count );
virtual bool Skip( int count );
virtual int64_t ByteCount() const;
/// Returns adler32 of all data read so far. Calling this makes backing up
/// for the previous Next() call impossible - the data has to be consumed
Adler32::Value getAdler32();
/// Performs a traditional read, for convenience purposes
void read( void * buf, size_t size );
/// Reads an adler32 value from the stream and compares with checkAdler32().
/// Throws an exception on mismatch
void checkAdler32();
/// Reads and discards the number of bytes equivalent to an IV size. This is
/// used when no IV is initially provided.
/// If there's no encryption key set, does nothing
void consumeRandomIv();
/// Closes the file
~InputStream() {}
private:
UnbufferedFile file;
UnbufferedFile::Offset filePos;
EncryptionKey const & key;
char iv[ Encryption::IvSize ];
std::vector< char > buffer;
char * start; /// Points to the start of the data currently held in buffer
size_t fill; /// Number of bytes held in buffer
size_t remainder; /// Number of bytes held in buffer just after the main
/// 'fill'-bytes portion. We have to keep those to implement
/// PKCS#7 padding
bool backedUp; /// True if the BackUp operation was performed, and the buffer
/// contents are therefore unconsumed
Adler32 adler32;
/// Decrypts 'fill' bytes at 'start', adjusting 'fill' and setting 'remainder'
void decrypt();
/// Only used by decrypt()
void doDecrypt();
};
class OutputStream: public google::protobuf::io::ZeroCopyOutputStream
{
public:
/// Creates the output file. If EncryptionKey contains no key, the output
/// won't be encrypted and iv would be ignored
OutputStream( char const * fileName, EncryptionKey const &, void const * iv );
virtual bool Next( void ** data, int * size );
virtual void BackUp( int count );
virtual int64_t ByteCount() const;
/// Returns adler32 of all data written so far. Calling this makes backing up
/// for the previous Next() call impossible - the data has to be consumed
Adler32::Value getAdler32();
/// Performs a traditional write, for convenience purposes
void write( void const * buf, size_t size );
/// Writes the current adler32 value returned by getAdler32() to the stream
void writeAdler32();
/// Writes the number of random bytes equivalent to an IV size. This is used
/// when no IV is initially provided, and provides an equivalent of having
/// a random IV when used just after the stream has been opened.
/// If there's no encryption key set, does nothing
void writeRandomIv();
/// Finishes writing and closes the file
~OutputStream();
private:
UnbufferedFile file;
UnbufferedFile::Offset filePos;
EncryptionKey const & key;
char iv[ Encryption::IvSize ];
std::vector< char > buffer;
char * start; /// Points to the start of the area currently available for
/// writing to in buffer
size_t avail; /// Number of bytes available for writing to in buffer
bool backedUp; /// True if the BackUp operation was performed, and the buffer
/// contents are therefore unconsumed
Adler32 adler32;
/// Encrypts and writes 'bytes' bytes from the beginning of the buffer.
/// 'bytes' must be non-zero and in multiples of BlockSize
void encryptAndWrite( size_t bytes );
};
}
#endif
|