#include "enctain.h"
#include <vector>
#include <map>
#include <memory>
#include <zlib.h>
#include "bytebuff.h"
#include "botan-1.6/include/base.h"
#include "botan-1.6/include/init.h"
#include "botan-1.6/include/rng.h"
#include "botan-1.6/include/lookup.h"
#include "botan-1.6/include/pkcs5.h"
#include "botan-1.6/include/crc32.h"
#include "botan-1.6/include/pipe.h"
#include "botan-1.6/include/buf_filt.h"
#include "botan-1.6/include/zlib.h"
#include "botan-1.6/include/bzip2.h"
namespace Enctain {
namespace internal {
class ContainerImpl
{
protected:
typedef std::map<std::string, std::string> propertymap_type;
class SubFile
{
public:
uint32_t storagesize;
uint32_t realsize;
uint32_t crc32;
union {
uint32_t flags;
struct {
uint8_t compression;
uint8_t encryption;
uint8_t reserved1;
uint8_t reserved2;
};
};
Botan::SecureVector<Botan::byte> encdata;
propertymap_type properties;
Botan::SecureVector<Botan::byte> data;
SubFile()
: storagesize(0), realsize(0), crc32(0), flags(0)
{
}
};
struct Header1
{
char signature[8];
uint16_t version_major;
uint16_t version_minor;
uint32_t unc_metalen;
} __attribute__((packed));
struct Header2
{
uint32_t mkd_iterations;
uint8_t mkd_salt[32];
uint8_t mkd_digest[32];
uint32_t mkk_iterations;
uint8_t mkk_salt[32];
uint32_t mki_iterations;
uint8_t mki_salt[32];
uint32_t keyslots;
} __attribute__((packed));
struct KeySlot
{
uint32_t iterations;
uint8_t salt[32];
uint8_t emasterkey[64];
} __attribute__((packed));
struct Header3
{
uint32_t metacomplen;
uint32_t metacrc32;
uint32_t padding1;
uint32_t padding2;
} __attribute__((packed));
static char m_filesignature[8];
unsigned int m_references;
bool m_modified;
Header2 m_header2;
Botan::SecureBuffer<Botan::byte, 64> m_masterkey;
std::vector<KeySlot> m_keyslots;
int m_usedkeyslot;
propertymap_type m_unc_properties;
propertymap_type m_enc_properties;
std::vector<SubFile> m_subfiles;
size_t m_written;
ProgressIndicator* m_progressindicator;
friend class ProgressTicker;
public:
ContainerImpl();
~ContainerImpl();
void Clear();
void IncReference();
unsigned int DecReference();
static void SetSignature(const char* sign);
static const char* GetErrorString(error_type e);
void Save(DataOutput& dataout);
void Load(DataInput& datain, const std::string& userkey);
void Loadv1(DataInput& datain, const std::string& userkey, const Header1& header1, class ProgressTicker& progress);
bool GetModified() const;
size_t GetLastWritten() const;
void SetProgressIndicator(ProgressIndicator* pi);
unsigned int CountKeySlots() const;
unsigned int AddKeySlot(const std::string& key);
void ChangeKeySlot(unsigned int slot, const std::string& key);
void DeleteKeySlot(unsigned int slot);
int GetUsedKeySlot() const;
void SetGlobalUnencryptedProperty(const std::string& key, const std::string& value);
const std::string& GetGlobalUnencryptedProperty(const std::string& key) const;
bool DeleteGlobalUnencryptedProperty(const std::string& key);
bool GetGlobalUnencryptedPropertyIndex(unsigned int propindex,
std::string& key, std::string& value) const;
void SetGlobalEncryptedProperty(const std::string& key, const std::string& value);
const std::string& GetGlobalEncryptedProperty(const std::string& key) const;
bool DeleteGlobalEncryptedProperty(const std::string& key);
bool GetGlobalEncryptedPropertyIndex(unsigned int propindex,
std::string& key, std::string& value) const;
unsigned int CountSubFile() const;
unsigned int AppendSubFile();
unsigned int InsertSubFile(unsigned int subfileindex);
bool DeleteSubFile(unsigned int subfileindex);
void SetSubFileProperty(unsigned int subfileindex, const std::string& key, const std::string& value);
const std::string& GetSubFileProperty(unsigned int subfileindex, const std::string& key) const;
bool DeleteSubFileProperty(unsigned int subfileindex, const std::string& key);
bool GetSubFilePropertyIndex(unsigned int subfileindex, unsigned int propindex,
std::string& key, std::string& value) const;
uint32_t GetSubFileStorageSize(unsigned int subfileindex) const;
uint32_t GetSubFileSize(unsigned int subfileindex) const;
encryption_type GetSubFileEncryption(unsigned int subfileindex) const;
compression_type GetSubFileCompression(unsigned int subfileindex) const;
void SetSubFileEncryption(unsigned int subfileindex, encryption_type c);
void SetSubFileCompression(unsigned int subfileindex, compression_type c);
void SetSubFileCompressionEncryption(unsigned int subfileindex, compression_type comp, encryption_type enc);
void GetSubFileData(unsigned int subfileindex, class DataOutput& dataout) const;
void GetSubFileData(unsigned int subfileindex, std::string& data) const;
void SetSubFileData(unsigned int subfileindex, const void* data, unsigned int datalen);
};
#define AssertException(x) do { if (!(x)) { throw(InternalError(ETE_TEXT, "Assertion failed: " #x)); } } while(0)
ContainerImpl::ContainerImpl()
: m_references(1),
m_modified(false),
m_usedkeyslot(-1),
m_written(0),
m_progressindicator(NULL)
{
memset(&m_header2, 0, sizeof(m_header2));
}
ContainerImpl::~ContainerImpl()
{
m_masterkey.clear();
m_keyslots.clear();
memset(&m_header2, 0, sizeof(m_header2));
}
void ContainerImpl::Clear()
{
m_modified = false;
memset(&m_header2, 0, sizeof(m_header2));
m_masterkey.clear();
m_keyslots.clear();
m_usedkeyslot = -1;
m_unc_properties.clear();
m_enc_properties.clear();
m_subfiles.clear();
m_written = 0;
}
void ContainerImpl::IncReference()
{
++m_references;
}
unsigned int ContainerImpl::DecReference()
{
return --m_references;
}
class ProgressTicker
{
private:
const ContainerImpl& m_cnt;
size_t m_current;
public:
ProgressTicker(const ContainerImpl& c,
const char* pitext, progress_indicator_type pitype,
size_t value, size_t limit)
: m_cnt(c)
{
if (m_cnt.m_progressindicator)
m_cnt.m_progressindicator->ProgressStart(pitext, pitype, value, limit);
m_current = value;
}
void Restart(const char* pitext, progress_indicator_type pitype,
size_t value, size_t limit)
{
if (m_cnt.m_progressindicator)
m_cnt.m_progressindicator->ProgressStart(pitext, pitype, value, limit);
m_current = value;
}
void Update(size_t value)
{
if (m_cnt.m_progressindicator)
m_cnt.m_progressindicator->ProgressUpdate(value);
m_current = value;
}
void Add(size_t increment)
{
m_current += increment;
if (m_cnt.m_progressindicator)
m_cnt.m_progressindicator->ProgressUpdate(m_current);
}
~ProgressTicker()
{
if (m_cnt.m_progressindicator)
m_cnt.m_progressindicator->ProgressStop();
}
};
char ContainerImpl::m_filesignature[8] =
{ 'C', 'r', 'y', 'p', 't', 'o', 'T', 'E' };
void ContainerImpl::SetSignature(const char* sign)
{
unsigned int i = 0;
while(sign[i] != 0 && i < 8) {
m_filesignature[i] = sign[i];
++i;
}
while(i < 8) {
m_filesignature[i] = 0;
++i;
}
}
const char* ContainerImpl::GetErrorString(error_type e)
{
switch(e)
{
case ETE_SUCCESS:
return "Success.";
case ETE_TEXT:
return "Generic text message error.";
case ETE_OUTPUT_ERROR:
return "DataOutput stream error.";
case ETE_INPUT_ERROR:
return "DataInput stream error.";
case ETE_SAVE_NO_KEYSLOTS:
return "Error saving container: no encryption key slots set!";
case ETE_LOAD_HEADER1:
return "Error loading container: could not read primary header.";
case ETE_LOAD_HEADER1_SIGNATURE:
return "Error loading container: could not read primary header, invalid signature.";
case ETE_LOAD_HEADER1_VERSION:
return "Error loading container: could not read primary header, invalid version.";
case ETE_LOAD_HEADER1_METADATA:
return "Error loading container: could not read primary header, invalid metadata.";
case ETE_LOAD_HEADER1_METADATA_PARSE:
return "Error loading container: could not read primary header, metadata parse failed.";
case ETE_LOAD_HEADER2:
return "Error loading container: could not read key slots header.";
case ETE_LOAD_HEADER2_NO_KEYSLOTS:
return "Error loading container: file contains no key slots for decryption.";
case ETE_LOAD_HEADER2_KEYSLOTS:
return "Error loading container: could not read key slots header, error reading key slot material.";
case ETE_LOAD_HEADER2_INVALID_KEY:
return "Error loading container: supplied key matches no key slot in header, check password.";
case ETE_LOAD_HEADER3:
return "Error loading container: could not read data header.";
case ETE_LOAD_HEADER3_ENCRYPTION:
return "Error loading container: could not read data header, check encryption key.";
case ETE_LOAD_HEADER3_METADATA:
return "Error loading container: could not read data header, invalid metadata.";
case ETE_LOAD_HEADER3_METADATA_CHECKSUM:
return "Error loading container: could not read data header, metadata CRC32 checksum mismatch.";
case ETE_LOAD_HEADER3_METADATA_PARSE:
return "Error loading container: could not read data header, metadata parse failed.";
case ETE_LOAD_SUBFILE:
return "Error loading container: could not read encrypted subfile data.";
case ETE_LOAD_CHECKSUM:
return "Error loading container: CRC32 checksum mismatch, file possibly corrupt.";
case ETE_KEYSLOT_INVALID_INDEX:
return "Invalid encryption key slot index.";
case ETE_SUBFILE_INVALID_INDEX:
return "Invalid subfile index.";
case ETE_SUBFILE_CHECKSUM:
return "Error in subfile: CRC32 checksum mismatch, data possibly corrupt.";
case ETE_SUBFILE_INVALID_COMPRESSION:
return "Unknown subfile compression algorithm.";
case ETE_SUBFILE_INVALID_ENCRYPTION:
return "Unknown subfile encryption cipher.";
case ETE_Z_UNKNOWN:
return "Error in zlib: unknown error.";
case ETE_Z_OK:
return "Error in zlib: success.";
case ETE_Z_NEED_DICT:
return "Error in zlib: need dictionary.";
case ETE_Z_STREAM_END:
return "Error in zlib: stream end.";
case ETE_Z_ERRNO:
return "Error in zlib: file error.";
case ETE_Z_STREAM_ERROR:
return "Error in zlib: stream error.";
case ETE_Z_DATA_ERROR:
return "Error in zlib: data error.";
case ETE_Z_MEM_ERROR:
return "Error in zlib: insufficient memory.";
case ETE_Z_BUF_ERROR:
return "Error in zlib: buffer error.";
case ETE_Z_VERSION_ERROR:
return "Error in zlib: incompatible version.";
}
return "Unknown error code.";
}
static error_type ErrorCodeFromZLibError(int ret)
{
switch(ret)
{
default: return ETE_Z_UNKNOWN;
case Z_NEED_DICT: return ETE_Z_NEED_DICT;
case Z_STREAM_END: return ETE_Z_STREAM_END;
case Z_OK: return ETE_Z_OK;
case Z_ERRNO: return ETE_Z_ERRNO;
case Z_STREAM_ERROR: return ETE_Z_STREAM_ERROR;
case Z_DATA_ERROR: return ETE_Z_DATA_ERROR;
case Z_MEM_ERROR: return ETE_Z_MEM_ERROR;
case Z_BUF_ERROR: return ETE_Z_BUF_ERROR;
case Z_VERSION_ERROR: return ETE_Z_VERSION_ERROR;
}
}
static inline uint32_t random_iterations()
{
uint16_t iterations;
Botan::Global_RNG::randomize((Botan::byte*)&iterations, sizeof(iterations));
return (iterations % 10000) + 1000;
}
static inline uint32_t botan_crc32(Botan::byte* data, uint32_t datalen)
{
Botan::SecureVector<Botan::byte> crc = Botan::CRC32().process(data, datalen);
AssertException(crc.size() == 4);
return *(uint32_t*)crc.begin();
}
class DataSinkSecureVector : public Botan::DataSink
{
private:
Botan::SecureVector<Botan::byte>& m_secmem;
public:
DataSinkSecureVector(Botan::SecureVector<Botan::byte>& secmem)
: m_secmem(secmem)
{ }
void write(const Botan::byte out[], Botan::u32bit length)
{
m_secmem.append(out, length);
}
};
class DataSink2DataOutput : public Botan::DataSink
{
private:
DataOutput& m_dataout;
uint32_t m_written;
ProgressTicker& m_progress;
public:
DataSink2DataOutput(DataOutput& do_, ProgressTicker& progress)
: m_dataout(do_), m_written(0), m_progress(progress)
{ }
void write(const Botan::byte out[], Botan::u32bit length)
{
if (!m_dataout.Output(out, length))
throw(RuntimeError(ETE_OUTPUT_ERROR));
m_written += length;
m_progress.Add(length);
}
uint32_t get_written() const { return m_written; }
};
class NullBufferingFilter : public Botan::Buffering_Filter
{
public:
const unsigned int m_blocksize;
NullBufferingFilter(uint32_t blocksize)
: Buffering_Filter(blocksize), m_blocksize(blocksize)
{ }
protected:
virtual void main_block(const Botan::byte input[])
{
send(input, m_blocksize);
}
virtual void final_block(const Botan::byte input[], Botan::u32bit length)
{
send(input, length);
}
};
class DataOutputHashChain : public DataOutput
{
public:
DataOutput& m_chain;
Botan::HashFunction& m_hash;
DataOutputHashChain(DataOutput& chain, Botan::HashFunction& hash)
: m_chain(chain), m_hash(hash)
{ }
bool Output(const void* data, size_t datalen)
{
m_hash.update((const Botan::byte*)data, datalen);
return m_chain.Output(data, datalen);
}
};
class DataInputHashChain : public DataInput
{
public:
DataInput& m_chain;
Botan::HashFunction& m_hash;
DataInputHashChain(DataInput& chain, Botan::HashFunction& hash)
: m_chain(chain), m_hash(hash)
{ }
unsigned int Input(void* data, size_t maxlen)
{
unsigned int rb = m_chain.Input(data, maxlen);
m_hash.update((Botan::byte*)data, rb);
return rb;
}
};
void ContainerImpl::Save(DataOutput& dataout_)
{
m_written = 0;
if (m_keyslots.empty())
throw(ProgramError(ETE_SAVE_NO_KEYSLOTS));
size_t subfiletotal = 0;
for (unsigned int si = 0; si < m_subfiles.size(); ++si)
{
subfiletotal += m_subfiles[si].storagesize;
}
size_t esttotal = sizeof(Header1)
+ 100
+ sizeof(Header2)
+ m_keyslots.size() * sizeof(KeySlot)
+ sizeof(Header3)
+ m_subfiles.size() * 50
+ subfiletotal;
ProgressTicker progress(*this,
"Saving Container", PI_SAVE_CONTAINER,
0, esttotal);
Botan::CRC32 crc32all;
DataOutputHashChain dataout(dataout_, crc32all);
{
ByteBuffer unc_metadata;
unc_metadata.put<unsigned int>(m_unc_properties.size());
for (propertymap_type::const_iterator pi = m_unc_properties.begin();
pi != m_unc_properties.end(); ++pi)
{
unc_metadata.put<std::string>(pi->first);
unc_metadata.put<std::string>(pi->second);
}
struct Header1 header1;
memcpy(header1.signature, m_filesignature, 8);
header1.version_major = 1;
header1.version_minor = 0;
header1.unc_metalen = unc_metadata.size();
if (!dataout.Output(&header1, sizeof(header1)))
throw(RuntimeError(ETE_OUTPUT_ERROR));
if (!dataout.Output(unc_metadata.data(), unc_metadata.size()))
throw(RuntimeError(ETE_OUTPUT_ERROR));
m_written += sizeof(header1) + unc_metadata.size();
progress.Update(m_written);
}
{
m_header2.keyslots = m_keyslots.size();
if (!dataout.Output(&m_header2, sizeof(m_header2)))
throw(RuntimeError(ETE_OUTPUT_ERROR));
for (unsigned int i = 0; i < m_keyslots.size(); ++i)
{
if (!dataout.Output(&m_keyslots[i], sizeof(m_keyslots[i])))
throw(RuntimeError(ETE_OUTPUT_ERROR));
}
}
ByteBuffer metadata;
metadata.put<unsigned int>(m_enc_properties.size());
for (propertymap_type::const_iterator pi = m_enc_properties.begin();
pi != m_enc_properties.end(); ++pi)
{
metadata.put<std::string>(pi->first);
metadata.put<std::string>(pi->second);
}
metadata.put<unsigned int>(m_subfiles.size());
for (unsigned int si = 0; si < m_subfiles.size(); ++si)
{
metadata.put<unsigned int>(m_subfiles[si].storagesize);
metadata.put<unsigned int>(m_subfiles[si].realsize);
metadata.put<unsigned int>(m_subfiles[si].flags);
metadata.put<unsigned int>(m_subfiles[si].crc32);
metadata.put<unsigned int>(m_subfiles[si].encdata.size());
metadata.append(m_subfiles[si].encdata.begin(), m_subfiles[si].encdata.size());
metadata.put<unsigned int>(m_subfiles[si].properties.size());
for (propertymap_type::const_iterator pi = m_subfiles[si].properties.begin();
pi != m_subfiles[si].properties.end(); ++pi)
{
metadata.put<std::string>(pi->first);
metadata.put<std::string>(pi->second);
}
}
ByteBuffer metadata_compressed;
{
z_stream zs;
memset(&zs, 0, sizeof(zs));
int ret = deflateInit(&zs, 9);
if (ret != Z_OK)
throw(InternalError( ErrorCodeFromZLibError(ret) ));
zs.next_in = metadata.data();
zs.avail_in = metadata.size();
char buffer[65536];
while (ret == Z_OK)
{
zs.next_out = (Bytef*)buffer;
zs.avail_out = sizeof(buffer);
ret = deflate(&zs, Z_FINISH);
if (metadata_compressed.size() < zs.total_out)
{
metadata_compressed.append(buffer,
zs.total_out - metadata_compressed.size());
}
}
deflateEnd(&zs);
if (ret != Z_STREAM_END)
throw(InternalError( ErrorCodeFromZLibError(ret) ));
metadata_compressed.align(16);
}
{
struct Header3 header3;
memset(&header3, 0, sizeof(header3));
header3.metacomplen = metadata_compressed.size();
header3.metacrc32 = botan_crc32(metadata.data(), metadata.size());
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
pbkdf.set_iterations(m_header2.mkk_iterations);
pbkdf.change_salt(m_header2.mkk_salt, sizeof(m_header2.mkk_salt));
Botan::OctetString enckey = pbkdf.derive_key(32, m_masterkey);
pbkdf.set_iterations(m_header2.mki_iterations);
pbkdf.change_salt(m_header2.mki_salt, sizeof(m_header2.mki_salt));
Botan::OctetString enciv = pbkdf.derive_key(16, m_masterkey);
DataSink2DataOutput* datasink;
Botan::Pipe pipe(
Botan::get_cipher("Serpent/CBC/NoPadding", enckey, enciv, Botan::ENCRYPTION),
new NullBufferingFilter(4096),
(datasink = new DataSink2DataOutput(dataout, progress))
);
pipe.process_msg((Botan::byte*)&header3, sizeof(header3));
pipe.process_msg(metadata_compressed.data(), metadata_compressed.size());
m_written += datasink->get_written();
}
esttotal = m_written + subfiletotal;
progress.Restart("Saving Container", PI_SAVE_CONTAINER, m_written, esttotal);
for (unsigned int si = 0; si < m_subfiles.size(); ++si)
{
AssertException(m_subfiles[si].storagesize == m_subfiles[si].data.size());
if (!dataout.Output(m_subfiles[si].data.begin(), m_subfiles[si].data.size()))
throw(RuntimeError(ETE_OUTPUT_ERROR));
m_written += m_subfiles[si].storagesize;
progress.Update(m_written);
}
{
Botan::SecureVector<Botan::byte> crc32val = crc32all.final();
AssertException(crc32val.size() == 4);
if (!dataout.Output(crc32val.begin(), crc32val.size()))
throw(RuntimeError(ETE_OUTPUT_ERROR));
m_written += 4;
}
m_modified = false;
}
void ContainerImpl::Load(DataInput& datain, const std::string& userkey)
{
ProgressTicker progress(*this,
"Loading Container", PI_LOAD_CONTAINER,
0, 10240);
struct Header1 header1;
if (datain.Input(&header1, sizeof(header1)) != sizeof(header1))
throw(RuntimeError(ETE_LOAD_HEADER1));
if (memcmp(header1.signature, m_filesignature, 8) != 0)
throw(RuntimeError(ETE_LOAD_HEADER1_SIGNATURE));
if (header1.version_major == 1) {
Loadv1(datain, userkey, header1, progress);
}
else {
throw(RuntimeError(ETE_LOAD_HEADER1_VERSION));
}
}
void ContainerImpl::Loadv1(DataInput& datain_, const std::string& userkey, const Header1& header1, class ProgressTicker& progress)
{
if (header1.version_minor != 0) {
throw(RuntimeError(ETE_LOAD_HEADER1_VERSION));
}
m_modified = true;
unsigned int readbyte = sizeof(Header1);
Botan::CRC32 crc32all;
crc32all.update((Botan::byte*)&header1, sizeof(header1));
DataInputHashChain datain(datain_, crc32all);
{
ByteBuffer unc_metadata;
unc_metadata.alloc(header1.unc_metalen);
if (datain.Input(unc_metadata.data(), header1.unc_metalen) != header1.unc_metalen)
throw(RuntimeError(ETE_LOAD_HEADER1_METADATA));
readbyte += header1.unc_metalen;
unc_metadata.set_size(header1.unc_metalen);
try
{
unsigned int gpropsize = unc_metadata.get<unsigned int>();
m_unc_properties.clear();
for (unsigned int pi = 0; pi < gpropsize; ++pi)
{
std::string key = unc_metadata.get<std::string>();
std::string val = unc_metadata.get<std::string>();
m_unc_properties.insert( propertymap_type::value_type(key, val) );
}
}
catch (std::underflow_error& e)
{
throw(RuntimeError(ETE_LOAD_HEADER1_METADATA_PARSE));
}
}
progress.Update(readbyte);
{
if (datain.Input(&m_header2, sizeof(m_header2)) != sizeof(m_header2))
throw(RuntimeError(ETE_LOAD_HEADER2));
if (m_header2.keyslots == 0)
throw(RuntimeError(ETE_LOAD_HEADER2_NO_KEYSLOTS));
m_keyslots.clear();
for (unsigned int i = 0; i < m_header2.keyslots; ++i)
{
m_keyslots.push_back(KeySlot());
KeySlot& newkeyslot = m_keyslots.back();
if (datain.Input(&newkeyslot, sizeof(newkeyslot)) != sizeof(newkeyslot))
throw(RuntimeError(ETE_LOAD_HEADER2_KEYSLOTS));
}
readbyte += sizeof(m_header2) + m_header2.keyslots * sizeof(KeySlot);
}
progress.Update(readbyte);
{
Botan::SecureVector<Botan::byte> userkeyvector((Botan::byte*)userkey.data(), userkey.size());
unsigned int ks;
for (ks = 0; ks < m_header2.keyslots; ++ks)
{
KeySlot& keyslot = m_keyslots[ks];
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
pbkdf.set_iterations(keyslot.iterations);
pbkdf.change_salt(keyslot.salt, sizeof(keyslot.salt));
Botan::OctetString enckey = pbkdf.derive_key(32, userkeyvector);
Botan::Pipe pipe( Botan::get_cipher("Serpent/ECB/NoPadding", enckey, Botan::DECRYPTION) );
pipe.process_msg((Botan::byte*)keyslot.emasterkey, sizeof(keyslot.emasterkey));
Botan::SecureVector<Botan::byte> testmasterkey = pipe.read_all();
AssertException(testmasterkey.size() == m_masterkey.size());
pbkdf.set_iterations(m_header2.mkd_iterations);
pbkdf.change_salt(m_header2.mkd_salt, sizeof(m_header2.mkd_salt));
Botan::OctetString digest = pbkdf.derive_key(32, testmasterkey);
AssertException(digest.length() == sizeof(m_header2.mkd_digest));
if (Botan::same_mem(digest.begin(), m_header2.mkd_digest, sizeof(m_header2.mkd_digest)))
{
m_usedkeyslot = ks;
m_masterkey.set(testmasterkey.begin(), testmasterkey.size());
break;
}
}
if (ks >= m_header2.keyslots)
throw(RuntimeError(ETE_LOAD_HEADER2_INVALID_KEY));
}
struct Header3 header3;
if (datain.Input(&header3, sizeof(header3)) != sizeof(header3))
throw(RuntimeError(ETE_LOAD_HEADER3));
readbyte += sizeof(header3);
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
pbkdf.set_iterations(m_header2.mkk_iterations);
pbkdf.change_salt(m_header2.mkk_salt, sizeof(m_header2.mkk_salt));
Botan::OctetString enckey = pbkdf.derive_key(32, m_masterkey);
pbkdf.set_iterations(m_header2.mki_iterations);
pbkdf.change_salt(m_header2.mki_salt, sizeof(m_header2.mki_salt));
Botan::OctetString enciv = pbkdf.derive_key(16, m_masterkey);
Botan::Pipe pipe( Botan::get_cipher("Serpent/CBC/NoPadding", enckey, enciv, Botan::DECRYPTION) );
pipe.process_msg((Botan::byte*)&header3, sizeof(header3));
if (pipe.read((Botan::byte*)&header3, sizeof(header3)) != sizeof(header3))
throw(RuntimeError(ETE_LOAD_HEADER3_ENCRYPTION));
ByteBuffer metadata_compressed;
metadata_compressed.alloc(header3.metacomplen);
if (datain.Input(metadata_compressed.data(), header3.metacomplen) != header3.metacomplen)
throw(RuntimeError(ETE_LOAD_HEADER3_METADATA));
readbyte += header3.metacomplen;
metadata_compressed.set_size(header3.metacomplen);
pipe.process_msg(metadata_compressed.data(), metadata_compressed.size());
if (pipe.read(metadata_compressed.data(), metadata_compressed.size(), 1) != metadata_compressed.size())
throw(RuntimeError(ETE_LOAD_HEADER3_ENCRYPTION));
progress.Restart("Loading Container", PI_LOAD_CONTAINER,
readbyte, readbyte * 10);
ByteBuffer metadata;
{
z_stream zs;
memset(&zs, 0, sizeof(zs));
int ret = inflateInit(&zs);
if (ret != Z_OK)
throw(RuntimeError( ErrorCodeFromZLibError(ret) ));
zs.next_in = metadata_compressed.data();
zs.avail_in = metadata_compressed.size();
char buffer[65536];
while (ret == Z_OK)
{
zs.next_out = (Bytef*)buffer;
zs.avail_out = sizeof(buffer);
ret = inflate(&zs, 0);
if (metadata.size() < zs.total_out) {
metadata.append(buffer, zs.total_out - metadata.size());
}
}
if (ret != Z_STREAM_END)
throw(RuntimeError( ErrorCodeFromZLibError(ret) ));
inflateEnd(&zs);
uint32_t testcrc32 = botan_crc32(metadata.data(), metadata.size());
if (testcrc32 != header3.metacrc32)
throw(RuntimeError(ETE_LOAD_HEADER3_METADATA_CHECKSUM));
}
try
{
unsigned int gpropsize = metadata.get<unsigned int>();
m_enc_properties.clear();
for (unsigned int pi = 0; pi < gpropsize; ++pi)
{
std::string key = metadata.get<std::string>();
std::string val = metadata.get<std::string>();
m_enc_properties.insert( propertymap_type::value_type(key, val) );
}
unsigned int subfilenum = metadata.get<unsigned int>();
m_subfiles.clear();
for (unsigned int si = 0; si < subfilenum; ++si)
{
m_subfiles.push_back(SubFile());
SubFile& subfile = m_subfiles.back();
subfile.storagesize = metadata.get<unsigned int>();
subfile.realsize = metadata.get<unsigned int>();
subfile.flags = metadata.get<unsigned int>();
subfile.crc32 = metadata.get<unsigned int>();
unsigned int encdatalen = metadata.get<unsigned int>();
subfile.encdata.create(encdatalen);
metadata.get(subfile.encdata.begin(), encdatalen);
unsigned int fpropsize = metadata.get<unsigned int>();
for (unsigned int pi = 0; pi < fpropsize; ++pi)
{
std::string key = metadata.get<std::string>();
std::string val = metadata.get<std::string>();
subfile.properties.insert( propertymap_type::value_type(key, val) );
}
}
}
catch (std::underflow_error& e)
{
throw(InternalError(ETE_LOAD_HEADER3_METADATA_PARSE));
}
size_t subfiletotal = 0;
for (unsigned int si = 0; si < m_subfiles.size(); ++si)
{
subfiletotal += m_subfiles[si].storagesize;
}
progress.Restart("Loading Container", PI_LOAD_CONTAINER,
readbyte, readbyte + subfiletotal);
for (unsigned int si = 0; si < m_subfiles.size(); ++si)
{
SubFile& subfile = m_subfiles[si];
subfile.data.create( subfile.storagesize );
unsigned int rb = datain.Input(subfile.data.begin(), subfile.storagesize);
if (rb != subfile.storagesize)
throw(RuntimeError(ETE_LOAD_SUBFILE));
readbyte += rb;
progress.Update(readbyte);
}
{
Botan::SecureVector<Botan::byte> crc32val = crc32all.final();
AssertException(crc32val.size() == 4);
uint32_t crc32file;
if (datain.Input(&crc32file, sizeof(crc32file)) != sizeof(crc32file))
throw(RuntimeError(ETE_LOAD_CHECKSUM));
if (crc32file != *(uint32_t*)crc32val.begin())
throw(RuntimeError(ETE_LOAD_CHECKSUM));
}
m_modified = false;
}
bool ContainerImpl::GetModified() const
{
return m_modified;
}
size_t ContainerImpl::GetLastWritten() const
{
return m_written;
}
void ContainerImpl::SetProgressIndicator(ProgressIndicator* pi)
{
m_progressindicator = pi;
}
unsigned int ContainerImpl::CountKeySlots() const
{
return m_keyslots.size();
}
unsigned int ContainerImpl::AddKeySlot(const std::string& key)
{
if (m_keyslots.empty())
{
Botan::Global_RNG::randomize(m_masterkey.begin(), m_masterkey.size());
m_header2.mkd_iterations = random_iterations();
Botan::Global_RNG::randomize(m_header2.mkd_salt, sizeof(m_header2.mkd_salt));
m_header2.mkk_iterations = random_iterations();
Botan::Global_RNG::randomize(m_header2.mkk_salt, sizeof(m_header2.mkk_salt));
m_header2.mki_iterations = random_iterations();
Botan::Global_RNG::randomize(m_header2.mki_salt, sizeof(m_header2.mki_salt));
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
pbkdf.set_iterations(m_header2.mkd_iterations);
pbkdf.change_salt(m_header2.mkd_salt, sizeof(m_header2.mkd_salt));
Botan::OctetString digest = pbkdf.derive_key(32, m_masterkey);
memcpy(m_header2.mkd_digest, digest.begin(), sizeof(m_header2.mkd_digest));
}
KeySlot newslot;
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
newslot.iterations = random_iterations();
Botan::Global_RNG::randomize(newslot.salt, sizeof(newslot.salt));
pbkdf.set_iterations(newslot.iterations);
pbkdf.change_salt(newslot.salt, sizeof(newslot.salt));
Botan::OctetString enckey = pbkdf.derive_key(32, Botan::SecureVector<Botan::byte>((Botan::byte*)key.data(), key.size()));
Botan::Pipe pipe( Botan::get_cipher("Serpent/ECB/NoPadding", enckey, Botan::ENCRYPTION) );
pipe.process_msg(m_masterkey);
Botan::SecureVector<Botan::byte> ciphertext = pipe.read_all();
AssertException(ciphertext.size() == sizeof(newslot.emasterkey));
memcpy(newslot.emasterkey, ciphertext.begin(), sizeof(newslot.emasterkey));
m_keyslots.push_back(newslot);
m_modified = true;
return m_keyslots.size() - 1;
}
void ContainerImpl::ChangeKeySlot(unsigned int slot, const std::string& key)
{
if (slot >= m_keyslots.size())
throw(ProgramError(ETE_KEYSLOT_INVALID_INDEX));
KeySlot& keyslot = m_keyslots[slot];
Botan::PKCS5_PBKDF2 pbkdf("SHA-256");
keyslot.iterations = random_iterations();
Botan::Global_RNG::randomize(keyslot.salt, sizeof(keyslot.salt));
pbkdf.set_iterations(keyslot.iterations);
pbkdf.change_salt(keyslot.salt, sizeof(keyslot.salt));
Botan::OctetString enckey = pbkdf.derive_key(32, Botan::SecureVector<Botan::byte>((Botan::byte*)key.data(), key.size()));
Botan::Pipe pipe( Botan::get_cipher("Serpent/ECB/NoPadding", enckey, Botan::ENCRYPTION) );
pipe.process_msg(m_masterkey);
Botan::SecureVector<Botan::byte> ciphertext = pipe.read_all();
AssertException(ciphertext.size() == sizeof(keyslot.emasterkey));
memcpy(keyslot.emasterkey, ciphertext.begin(), sizeof(keyslot.emasterkey));
m_modified = true;
}
void ContainerImpl::DeleteKeySlot(unsigned int slot)
{
if (slot >= m_keyslots.size())
throw(ProgramError(ETE_KEYSLOT_INVALID_INDEX));
if (m_usedkeyslot == (int)slot)
m_usedkeyslot = -1;
else if (m_usedkeyslot > (int)slot)
--m_usedkeyslot;
m_keyslots.erase(m_keyslots.begin() + slot);
m_modified = true;
}
int ContainerImpl::GetUsedKeySlot() const
{
return m_usedkeyslot;
}
void ContainerImpl::SetGlobalUnencryptedProperty(const std::string& key, const std::string& value)
{
m_unc_properties[key] = value;
m_modified = true;
}
const std::string& ContainerImpl::GetGlobalUnencryptedProperty(const std::string& key) const
{
propertymap_type::const_iterator pi = m_unc_properties.find(key);
if (pi != m_unc_properties.end()) {
return pi->second;
}
else {
static const std::string zerostring;
return zerostring;
}
}
bool ContainerImpl::DeleteGlobalUnencryptedProperty(const std::string& key)
{
return (m_unc_properties.erase(key) > 0) && (m_modified = true);
}
bool ContainerImpl::GetGlobalUnencryptedPropertyIndex(unsigned int propindex, std::string& key, std::string& value) const
{
if (propindex >= m_unc_properties.size()) return false;
propertymap_type::const_iterator pi = m_unc_properties.begin();
for(unsigned int i = 0; i < propindex; ++i) ++pi;
key = pi->first;
value = pi->second;
return true;
}
void ContainerImpl::SetGlobalEncryptedProperty(const std::string& key, const std::string& value)
{
m_enc_properties[key] = value;
m_modified = true;
}
const std::string& ContainerImpl::GetGlobalEncryptedProperty(const std::string& key) const
{
propertymap_type::const_iterator pi = m_enc_properties.find(key);
if (pi != m_enc_properties.end()) {
return pi->second;
}
else {
static const std::string zerostring;
return zerostring;
}
}
bool ContainerImpl::DeleteGlobalEncryptedProperty(const std::string& key)
{
return (m_enc_properties.erase(key) > 0) && (m_modified = true);
}
bool ContainerImpl::GetGlobalEncryptedPropertyIndex(unsigned int propindex, std::string& key, std::string& value) const
{
if (propindex >= m_enc_properties.size()) return false;
propertymap_type::const_iterator pi = m_enc_properties.begin();
for(unsigned int i = 0; i < propindex; ++i) ++pi;
key = pi->first;
value = pi->second;
return true;
}
unsigned int ContainerImpl::CountSubFile() const
{
return m_subfiles.size();
}
unsigned int ContainerImpl::AppendSubFile()
{
m_modified = true;
unsigned int si = m_subfiles.size();
m_subfiles.push_back( SubFile() );
return si;
}
unsigned int ContainerImpl::InsertSubFile(unsigned int subfileindex)
{
m_modified = true;
if (subfileindex < m_subfiles.size())
{
m_subfiles.insert(m_subfiles.begin() + subfileindex, SubFile());
return subfileindex;
}
else
{
return AppendSubFile();
}
}
bool ContainerImpl::DeleteSubFile(unsigned int subfileindex)
{
if (subfileindex < m_subfiles.size())
{
m_subfiles.erase(m_subfiles.begin() + subfileindex);
m_modified = true;
return true;
}
else
{
return false;
}
}
void ContainerImpl::SetSubFileProperty(unsigned int subfileindex, const std::string& key, const std::string& value)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
SubFile& subfile = m_subfiles[subfileindex];
subfile.properties[ key ] = value;
m_modified = true;
}
const std::string& ContainerImpl::GetSubFileProperty(unsigned int subfileindex, const std::string& key) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
const SubFile& subfile = m_subfiles[subfileindex];
propertymap_type::const_iterator pi = subfile.properties.find(key);
if (pi != subfile.properties.end()) {
return pi->second;
}
else {
static const std::string zerostring;
return zerostring;
}
}
bool ContainerImpl::DeleteSubFileProperty(unsigned int subfileindex, const std::string& key)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
m_modified = true;
SubFile& subfile = m_subfiles[subfileindex];
return (subfile.properties.erase(key) > 0);
}
bool ContainerImpl::GetSubFilePropertyIndex(unsigned int subfileindex, unsigned int propindex, std::string& key, std::string& value) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
const SubFile& subfile = m_subfiles[subfileindex];
if (propindex >= subfile.properties.size()) return false;
propertymap_type::const_iterator pi = subfile.properties.begin();
for(unsigned int i = 0; i < propindex; ++i) ++pi;
key = pi->first;
value = pi->second;
return true;
}
uint32_t ContainerImpl::GetSubFileStorageSize(unsigned int subfileindex) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
return m_subfiles[subfileindex].storagesize;
}
uint32_t ContainerImpl::GetSubFileSize(unsigned int subfileindex) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
return m_subfiles[subfileindex].realsize;
}
encryption_type ContainerImpl::GetSubFileEncryption(unsigned int subfileindex) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
return (encryption_type)m_subfiles[subfileindex].encryption;
}
compression_type ContainerImpl::GetSubFileCompression(unsigned int subfileindex) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
return (compression_type)m_subfiles[subfileindex].compression;
}
void ContainerImpl::SetSubFileEncryption(unsigned int subfileindex, encryption_type c)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
if (c < 0 || c > ENCRYPTION_SERPENT256)
throw(ProgramError(ETE_SUBFILE_INVALID_ENCRYPTION));
if (m_subfiles[subfileindex].encryption != c)
{
std::string data;
GetSubFileData(subfileindex, data);
m_subfiles[subfileindex].encryption = c;
SetSubFileData(subfileindex, data.data(), data.size());
}
}
void ContainerImpl::SetSubFileCompression(unsigned int subfileindex, compression_type c)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
if (c < 0 || c > COMPRESSION_BZIP2)
throw(ProgramError(ETE_SUBFILE_INVALID_COMPRESSION));
if (m_subfiles[subfileindex].compression != c)
{
std::string data;
GetSubFileData(subfileindex, data);
m_subfiles[subfileindex].compression = c;
SetSubFileData(subfileindex, data.data(), data.size());
}
}
void ContainerImpl::SetSubFileCompressionEncryption(unsigned int subfileindex, compression_type comp, encryption_type enc)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
if (comp < 0 || comp > COMPRESSION_BZIP2)
throw(ProgramError(ETE_SUBFILE_INVALID_COMPRESSION));
if (enc < 0 || enc > ENCRYPTION_SERPENT256)
throw(ProgramError(ETE_SUBFILE_INVALID_ENCRYPTION));
if (m_subfiles[subfileindex].encryption != enc || m_subfiles[subfileindex].compression != comp)
{
std::string data;
GetSubFileData(subfileindex, data);
m_subfiles[subfileindex].encryption = enc;
m_subfiles[subfileindex].compression = comp;
SetSubFileData(subfileindex, data.data(), data.size());
}
}
void ContainerImpl::SetSubFileData(unsigned int subfileindex, const void* data, unsigned int datalen)
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
SubFile& subfile = m_subfiles[subfileindex];
subfile.realsize = datalen;
ProgressTicker progress(*this,
"Saving SubFile", PI_SAVE_SUBFILE,
0, datalen);
std::auto_ptr<Botan::Filter> encryption_filter;
if (subfile.encryption == ENCRYPTION_NONE)
{
subfile.encdata.create(0);
}
else if (subfile.encryption == ENCRYPTION_SERPENT256)
{
subfile.encdata.create(32 + 16);
Botan::Global_RNG::randomize(subfile.encdata.begin(), subfile.encdata.size());
Botan::SecureBuffer<Botan::byte, 32> enckey(subfile.encdata.begin(), 32);
Botan::SecureBuffer<Botan::byte, 16> cbciv(subfile.encdata.begin() + 32, 16);
encryption_filter = std::auto_ptr<Botan::Filter>(
Botan::get_cipher("Serpent/CBC/PKCS7", enckey, cbciv, Botan::ENCRYPTION)
);
}
else
{
throw(RuntimeError(ETE_SUBFILE_INVALID_ENCRYPTION));
}
std::auto_ptr<Botan::Filter> compression_filter;
if (subfile.compression == COMPRESSION_NONE)
{
}
else if (subfile.compression == COMPRESSION_ZLIB)
{
compression_filter = std::auto_ptr<Botan::Filter>(
new Botan::Zlib_Compression(9)
);
}
else if (subfile.compression == COMPRESSION_BZIP2)
{
compression_filter = std::auto_ptr<Botan::Filter>(
new Botan::Bzip_Compression(9)
);
}
else
{
throw(RuntimeError(ETE_SUBFILE_INVALID_COMPRESSION));
}
subfile.data.create(datalen / 16);
subfile.data.create(0);
Botan::Filter* filters[4] = {
new Botan::Fork(compression_filter.release(),
new Botan::Hash_Filter("CRC32")),
encryption_filter.release(),
new NullBufferingFilter( std::max<unsigned int>(4096, datalen / 10) ),
new DataSinkSecureVector(subfile.data)
};
Botan::Pipe pipe(filters, 4);
pipe.start_msg();
for(unsigned int dataptr = 0; dataptr < datalen; dataptr += 8192)
{
pipe.write((const Botan::byte*)data + dataptr, std::min<unsigned int>(datalen - dataptr, 8192));
progress.Update(dataptr);
}
pipe.end_msg();
progress.Update(datalen);
Botan::SecureVector<Botan::byte> crc32 = pipe.read_all(1);
AssertException(crc32.size() == 4);
subfile.crc32 = *(const uint32_t*)crc32.begin();
subfile.storagesize = subfile.data.size();
m_modified = true;
}
struct DataOutputString : public DataOutput
{
std::string& str;
DataOutputString(std::string& s)
: str(s)
{
}
virtual bool Output(const void* data, size_t datalen)
{
str.append(static_cast<const char*>(data), datalen);
return true;
}
};
void ContainerImpl::GetSubFileData(unsigned int subfileindex, std::string& outstr) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
const SubFile& subfile = m_subfiles[subfileindex];
outstr.clear();
outstr.reserve(subfile.realsize);
DataOutputString dataout(outstr);
GetSubFileData(subfileindex, dataout);
}
void ContainerImpl::GetSubFileData(unsigned int subfileindex, class DataOutput& dataout) const
{
if (subfileindex >= m_subfiles.size())
throw(ProgramError(ETE_SUBFILE_INVALID_INDEX));
const SubFile& subfile = m_subfiles[subfileindex];
if (subfile.data.size() == 0) return;
AssertException(subfile.data.size() == subfile.storagesize);
ProgressTicker progress(*this,
"Loading SubFile", PI_LOAD_SUBFILE,
0, subfile.realsize);
std::auto_ptr<Botan::Filter> decryption_filter;
if (subfile.encryption == ENCRYPTION_NONE)
{
}
else if (subfile.encryption == ENCRYPTION_SERPENT256)
{
AssertException(subfile.encdata.size() == 32 + 16);
Botan::SecureBuffer<Botan::byte, 32> enckey(subfile.encdata.begin(), 32);
Botan::SecureBuffer<Botan::byte, 16> cbciv(subfile.encdata.begin() + 32, 16);
decryption_filter = std::auto_ptr<Botan::Filter>(
Botan::get_cipher("Serpent/CBC/PKCS7", enckey, cbciv, Botan::DECRYPTION)
);
}
else
{
throw(RuntimeError(ETE_SUBFILE_INVALID_ENCRYPTION));
}
std::auto_ptr<Botan::Filter> decompression_filter;
if (subfile.compression == COMPRESSION_NONE)
{
}
else if (subfile.compression == COMPRESSION_ZLIB)
{
decompression_filter = std::auto_ptr<Botan::Filter>(
new Botan::Zlib_Decompression()
);
}
else if (subfile.compression == COMPRESSION_BZIP2)
{
decompression_filter = std::auto_ptr<Botan::Filter>(
new Botan::Bzip_Decompression()
);
}
else
{
throw(RuntimeError(ETE_SUBFILE_INVALID_COMPRESSION));
}
Botan::Filter* filters[5] = {
decryption_filter.release(),
decompression_filter.release(),
new Botan::Fork(NULL,
new Botan::Hash_Filter("CRC32")),
new NullBufferingFilter(4096),
new DataSink2DataOutput(dataout, progress)
};
Botan::Pipe pipe(filters, 5);
pipe.process_msg(subfile.data);
Botan::SecureVector<Botan::byte> crc32v = pipe.read_all(1);
AssertException(crc32v.size() == 4);
uint32_t crc32 = *(const uint32_t*)crc32v.begin();
if (subfile.crc32 != crc32)
throw(RuntimeError(ETE_SUBFILE_CHECKSUM));
}
}
Exception::Exception(error_type ec, const std::string& m)
: m_ecode(ec), m_msg("Enctain: " + m)
{ }
RuntimeError::RuntimeError(error_type ec, const std::string& m)
: Exception(ec, "<RuntimeError> " + m)
{
}
RuntimeError::RuntimeError(error_type ec)
: Exception(ec, std::string("<RuntimeError> ") + Container::GetErrorString(ec))
{
}
ProgramError::ProgramError(error_type ec, const std::string& m)
: Exception(ec, "<ProgramError> " + m)
{
}
ProgramError::ProgramError(error_type ec)
: Exception(ec, std::string("<ProgramError> ") + Container::GetErrorString(ec))
{
}
InternalError::InternalError(error_type ec, const std::string& m)
: Exception(ec, "<InternalError> " + m)
{
}
InternalError::InternalError(error_type ec)
: Exception(ec, std::string("<InternalError> ") + Container::GetErrorString(ec))
{
}
Container::Container()
{
m_pimpl = new internal::ContainerImpl();
}
Container::~Container()
{
if (m_pimpl->DecReference() == 0)
delete m_pimpl;
}
Container::Container(const Container &cnt)
{
m_pimpl = cnt.m_pimpl;
m_pimpl->IncReference();
}
Container& Container::operator=(const Container &cnt)
{
if (&cnt == this) return *this;
if (m_pimpl->DecReference() == 0)
delete m_pimpl;
m_pimpl = cnt.m_pimpl;
m_pimpl->IncReference();
return *this;
}
void Container::SetSignature(const char* sign)
{
internal::ContainerImpl::SetSignature(sign);
}
const char* Container::GetErrorString(error_type e)
{
return internal::ContainerImpl::GetErrorString(e);
}
void Container::Save(DataOutput& dataout)
{
return m_pimpl->Save(dataout);
}
void Container::Load(DataInput& datain, const std::string& userkey)
{
return m_pimpl->Load(datain, userkey);
}
void Container::Clear()
{
return m_pimpl->Clear();
}
bool Container::GetModified() const
{
return m_pimpl->GetModified();
}
size_t Container::GetLastWritten() const
{
return m_pimpl->GetLastWritten();
}
void Container::SetProgressIndicator(ProgressIndicator* pi)
{
return m_pimpl->SetProgressIndicator(pi);
}
unsigned int Container::CountKeySlots() const
{
return m_pimpl->CountKeySlots();
}
unsigned int Container::AddKeySlot(const std::string& key)
{
return m_pimpl->AddKeySlot(key);
}
void Container::ChangeKeySlot(unsigned int slot, const std::string& key)
{
return m_pimpl->ChangeKeySlot(slot, key);
}
void Container::DeleteKeySlot(unsigned int slot)
{
return m_pimpl->DeleteKeySlot(slot);
}
int Container::GetUsedKeySlot() const
{
return m_pimpl->GetUsedKeySlot();
}
void Container::SetGlobalUnencryptedProperty(const std::string& key, const std::string& value)
{
return m_pimpl->SetGlobalUnencryptedProperty(key, value);
}
const std::string& Container::GetGlobalUnencryptedProperty(const std::string& key) const
{
return m_pimpl->GetGlobalUnencryptedProperty(key);
}
bool Container::DeleteGlobalUnencryptedProperty(const std::string& key)
{
return m_pimpl->DeleteGlobalUnencryptedProperty(key);
}
bool Container::GetGlobalUnencryptedPropertyIndex(unsigned int propindex, std::string& key, std::string& value) const
{
return m_pimpl->GetGlobalUnencryptedPropertyIndex(propindex, key, value);
}
void Container::SetGlobalEncryptedProperty(const std::string& key, const std::string& value)
{
return m_pimpl->SetGlobalEncryptedProperty(key, value);
}
const std::string& Container::GetGlobalEncryptedProperty(const std::string& key) const
{
return m_pimpl->GetGlobalEncryptedProperty(key);
}
bool Container::DeleteGlobalEncryptedProperty(const std::string& key)
{
return m_pimpl->DeleteGlobalEncryptedProperty(key);
}
bool Container::GetGlobalEncryptedPropertyIndex(unsigned int propindex, std::string& key, std::string& value) const
{
return m_pimpl->GetGlobalEncryptedPropertyIndex(propindex, key, value);
}
unsigned int Container::CountSubFile() const
{
return m_pimpl->CountSubFile();
}
unsigned int Container::AppendSubFile()
{
return m_pimpl->AppendSubFile();
}
unsigned int Container::InsertSubFile(unsigned int subfileindex)
{
return m_pimpl->InsertSubFile(subfileindex);
}
bool Container::DeleteSubFile(unsigned int subfileindex)
{
return m_pimpl->DeleteSubFile(subfileindex);
}
void Container::SetSubFileProperty(unsigned int subfileindex, const std::string& key, const std::string& value)
{
return m_pimpl->SetSubFileProperty(subfileindex, key, value);
}
const std::string& Container::GetSubFileProperty(unsigned int subfileindex, const std::string& key) const
{
return m_pimpl->GetSubFileProperty(subfileindex, key);
}
bool Container::DeleteSubFileProperty(unsigned int subfileindex, const std::string& key)
{
return m_pimpl->DeleteSubFileProperty(subfileindex, key);
}
bool Container::GetSubFilePropertyIndex(unsigned int subfileindex, unsigned int propindex, std::string& key, std::string& value) const
{
return m_pimpl->GetSubFilePropertyIndex(subfileindex, propindex, key, value);
}
uint32_t Container::GetSubFileStorageSize(unsigned int subfileindex) const
{
return m_pimpl->GetSubFileStorageSize(subfileindex);
}
uint32_t Container::GetSubFileSize(unsigned int subfileindex) const
{
return m_pimpl->GetSubFileSize(subfileindex);
}
encryption_type Container::GetSubFileEncryption(unsigned int subfileindex) const
{
return m_pimpl->GetSubFileEncryption(subfileindex);
}
compression_type Container::GetSubFileCompression(unsigned int subfileindex) const
{
return m_pimpl->GetSubFileCompression(subfileindex);
}
void Container::SetSubFileEncryption(unsigned int subfileindex, encryption_type c)
{
return m_pimpl->SetSubFileEncryption(subfileindex, c);
}
void Container::SetSubFileCompression(unsigned int subfileindex, compression_type c)
{
return m_pimpl->SetSubFileCompression(subfileindex, c);
}
void Container::SetSubFileCompressionEncryption(unsigned int subfileindex, compression_type comp, encryption_type enc)
{
return m_pimpl->SetSubFileCompressionEncryption(subfileindex, comp, enc);
}
void Container::SetSubFileData(unsigned int subfileindex, const void* data, unsigned int datalen)
{
return m_pimpl->SetSubFileData(subfileindex, data, datalen);
}
void Container::GetSubFileData(unsigned int subfileindex, std::string& outstr) const
{
return m_pimpl->GetSubFileData(subfileindex, outstr);
}
void Container::GetSubFileData(unsigned int subfileindex, class DataOutput& dataout) const
{
return m_pimpl->GetSubFileData(subfileindex, dataout);
}
}