6 #include <c++utilities/io/catchiofailure.h> 8 #include <openssl/conf.h> 9 #include <openssl/err.h> 10 #include <openssl/evp.h> 11 #include <openssl/rand.h> 37 PasswordFile::PasswordFile() :
38 m_freader(BinaryReader(&m_file)),
39 m_fwriter(BinaryWriter(&m_file))
41 m_file.exceptions(ios_base::failbit | ios_base::badbit);
49 m_freader(BinaryReader(&m_file)),
50 m_fwriter(BinaryWriter(&m_file))
52 m_file.exceptions(ios_base::failbit | ios_base::badbit);
62 m_freader(BinaryReader(&m_file)),
63 m_fwriter(BinaryWriter(&m_file))
65 m_file.exceptions(ios_base::failbit | ios_base::badbit);
67 memcpy(m_password, other.m_password, 32);
86 throwIoFailure(
"Unable to open file because path is emtpy.");
88 m_file.open(m_path, readOnly ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary);
89 m_file.seekg(0, ios_base::end);
90 if(m_file.tellg() == 0) {
91 throwIoFailure(
"File is empty.");
103 m_rootEntry.reset(
new NodeEntry(
"accounts"));
115 throwIoFailure(
"Unable to create file because path is empty.");
117 m_file.open(m_path, fstream::out | fstream::trunc | fstream::binary);
130 if(!m_file.is_open()) {
135 if(m_freader.readUInt32LE() != 0x7770616DU) {
139 uint32 version = m_freader.readUInt32LE();
140 if(version != 0x0U && version != 0x1U && version != 0x2U && version != 0x3U && version != 0x4U && version != 0x5U) {
145 bool compressionUsed;
146 if(version == 0x3U) {
147 byte flags = m_freader.readByte();
148 decrypterUsed = flags & 0x80;
149 ivUsed = flags & 0x40;
150 compressionUsed = flags & 0x20;
152 decrypterUsed = version >= 0x1U;
153 ivUsed = version == 0x2U;
154 compressionUsed =
false;
159 if(version >= 0x4U) {
160 uint16 extendedHeaderSize = m_freader.readUInt16BE();
161 m_extendedHeader = m_freader.readString(extendedHeaderSize);
164 fstream::pos_type headerSize = m_file.tellg();
165 m_file.seekg(0, ios_base::end);
166 fstream::pos_type
size = m_file.tellg();
167 m_file.seekg(headerSize, ios_base::beg);
171 if(decrypterUsed && ivUsed) {
172 if(size < aes256cbcIvSize) {
175 m_file.read(reinterpret_cast<char *>(iv), aes256cbcIvSize);
182 vector<char> rawbuff;
183 m_freader.read(rawbuff, size);
184 vector<char> decbuff;
187 EVP_CIPHER_CTX *ctx =
nullptr;
188 decbuff.resize(size + static_cast<fstream::pos_type>(32));
189 int outlen1, outlen2;
190 if ((ctx = EVP_CIPHER_CTX_new()) ==
nullptr 191 || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr, reinterpret_cast<unsigned const char *>(m_password), iv) != 1
192 || EVP_DecryptUpdate(ctx, reinterpret_cast<unsigned char *>(decbuff.data()), &outlen1, reinterpret_cast<unsigned char *>(rawbuff.data()), size) != 1
193 || EVP_DecryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(decbuff.data()) + outlen1, &outlen2) != 1) {
195 EVP_CIPHER_CTX_free(ctx);
198 unsigned long errorCode = ERR_get_error();
199 while(errorCode != 0) {
203 msg += ERR_error_string(errorCode, 0);
204 errorCode = ERR_get_error();
209 EVP_CIPHER_CTX_free(ctx);
211 size = outlen1 + outlen2;
214 decbuff.swap(rawbuff);
217 if(compressionUsed) {
221 uLongf decompressedSize = ConversionUtilities::LE::toUInt64(decbuff.data());
222 rawbuff.resize(decompressedSize);
223 switch(uncompress(reinterpret_cast<Bytef *>(rawbuff.data()), &decompressedSize, reinterpret_cast<Bytef *>(decbuff.data() + 8), size - static_cast<fstream::pos_type>(8))) {
225 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
227 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
229 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
231 decbuff.swap(rawbuff);
232 size = decompressedSize;
236 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
237 buffstr.write(decbuff.data(),
static_cast<streamsize
>(
size));
239 buffstr.seekg(0, ios_base::beg);
240 if(version >= 0x5u) {
241 uint16 extendedHeaderSize = m_freader.readUInt16BE();
242 m_encryptedExtendedHeader = m_freader.readString(extendedHeaderSize);
244 m_rootEntry.reset(
new NodeEntry(buffstr));
258 throw runtime_error(
"Root entry has not been created.");
261 if(m_file.is_open()) {
265 m_file.open(m_path, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);
267 m_fwriter.writeUInt32LE(0x7770616DU);
269 m_fwriter.writeUInt32LE(m_extendedHeader.empty() && m_encryptedExtendedHeader.empty() ? 0x3U : (m_encryptedExtendedHeader.empty() ? 0x4U : 0x5U));
272 flags |= 0x80 | 0x40;
277 m_fwriter.writeByte(flags);
279 if(!m_extendedHeader.empty()) {
280 m_fwriter.writeUInt16BE(m_extendedHeader.size());
281 m_fwriter.writeString(m_extendedHeader);
284 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
285 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
287 if(!m_encryptedExtendedHeader.empty()) {
288 m_fwriter.writeUInt16BE(m_encryptedExtendedHeader.size());
289 m_fwriter.writeString(m_encryptedExtendedHeader);
291 m_rootEntry->make(buffstr);
292 buffstr.seekp(0, ios_base::end);
293 stringstream::pos_type
size = buffstr.tellp();
296 vector<char> decbuff(size, 0);
297 buffstr.read(decbuff.data(),
size);
298 vector<char> encbuff;
301 uLongf compressedSize = compressBound(size);
302 encbuff.resize(8 + compressedSize);
303 ConversionUtilities::LE::getBytes(static_cast<uint64>(size), encbuff.data());
304 switch(compress(reinterpret_cast<Bytef *>(encbuff.data() + 8), &compressedSize, reinterpret_cast<Bytef *>(decbuff.data()), size)) {
306 throw runtime_error(
"Decompressing failed. The source buffer was too small.");
308 throw runtime_error(
"Decompressing failed. The destination buffer was too small.");
310 encbuff.swap(decbuff);
311 size = 8 + compressedSize;
317 EVP_CIPHER_CTX *ctx =
nullptr;
319 int outlen1, outlen2;
320 encbuff.resize(size + static_cast<fstream::pos_type>(32));
321 if (RAND_bytes(iv, aes256cbcIvSize) != 1
322 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr 323 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr, reinterpret_cast<unsigned const char *>(m_password), iv) != 1
324 || EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char *>(encbuff.data()), &outlen1, reinterpret_cast<unsigned char *>(decbuff.data()), size) != 1
325 || EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(encbuff.data()) + outlen1, &outlen2) != 1) {
327 EVP_CIPHER_CTX_free(ctx);
330 unsigned long errorCode = ERR_get_error();
331 while(errorCode != 0) {
335 msg += ERR_error_string(errorCode, 0);
336 errorCode = ERR_get_error();
341 EVP_CIPHER_CTX_free(ctx);
344 m_file.write(reinterpret_cast<char *>(iv), aes256cbcIvSize);
345 m_file.write(encbuff.data(),
static_cast<streamsize
>(outlen1 + outlen2));
349 m_file.write(decbuff.data(),
static_cast<streamsize
>(
size));
371 m_extendedHeader.clear();
372 m_encryptedExtendedHeader.clear();
384 throw runtime_error(
"Root entry has not been created.");
386 fstream output(targetPath.c_str(), ios_base::out);
387 function<void (int level)> indention = [&output] (
int level) {
388 for(
int i = 0; i < level; ++i) {
392 function<void (const Entry *entry, int level)> printNode;
393 printNode = [&output, &printNode, &indention] (
const Entry *entry,
int level) {
395 output <<
" - " << entry->label() << endl;
396 switch(entry->type()) {
398 for(
const Entry *child : static_cast<const NodeEntry *>(entry)->children()) {
399 printNode(child, level + 1);
403 for(
const Field &field : static_cast<const AccountEntry *>(entry)->fields()) {
405 output <<
" " << field.name();
406 for(
int i = field.name().length(); i < 15; ++i) {
409 output << field.value() << endl;
413 printNode(m_rootEntry.get(), 0);
426 m_file.seekg(0, ios_base::end);
429 fstream backupFile(m_path +
".backup", ios::out | ios::trunc | ios::binary);
430 backupFile.exceptions(ios_base::failbit | ios_base::badbit);
431 backupFile << m_file.rdbuf();
445 return m_rootEntry !=
nullptr;
453 return m_rootEntry.get();
461 return m_rootEntry.get();
469 if(m_file.is_open()) {
515 value.copy(m_password, 32, 0);
523 memset(m_password, 0, 32);
536 if(m_freader.readUInt32LE() != 0x7770616DU) {
540 uint32 version = m_freader.readUInt32LE();
541 if(version == 0x1U || version == 0x2U) {
543 }
else if(version == 0x3U) {
544 return m_freader.readByte() & 0x80;
555 return m_file.is_open();
566 m_file.seekg(0, ios::end);
567 return m_file.tellg();
PasswordFile()
Constructs a new password file.
void clear()
Closes the file if opened.
void exportToTextfile(const std::string &targetPath) const
Writes the current root entry to a plain text file.
The NodeEntry class acts as parent for other entries.
The PasswordFile class holds account information in the form of Entry and Field instances and provide...
bool isOpen() const
Returns an indication whether the file is open.
void load()
Reads the contents of the file.
void open(bool readOnly=false)
Opens the file.
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
void close()
Closes the file if currently opened.
void doBackup()
Creates a backup of the file.
bool hasRootEntry() const
Returns an indication whether a root entry is present.
Contains all IO related classes.
The Field class holds field information which consists of a name and a value and is able to serialize...
void clearPath()
Clears the current path.
const char * password() const
Returns the current password.
bool isEncryptionUsed()
Returns an indication whether encryption is used if the file is open; returns always false otherwise...
const std::string & path() const
Returns the current file path.
const unsigned int aes256cbcIvSize
~PasswordFile()
Closes the file if still opened and destroys the instance.
void setPath(const std::string &value)
Sets the current file path.
void create()
Creates the file.
size_t size()
Returns the size of the file if the file is open; returns always zero otherwise.
void generateRootEntry()
Generates a new root entry for the file.
The exception that is thrown when an encryption/decryption error occurs.
void setPassword(const std::string &value)
Sets the current password.
void save(bool useEncryption=true, bool useCompression=true)
Writes the current root entry to the file.
The exception that is thrown when a parsing error occurs.
void clearPassword()
Clears the current password.
void clearEntries()
Removes the root element if one is present.
Instances of the Entry class form a hierarchic data strucutre used to store account information...