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);
61 : m_path(other.m_path)
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"));
114 if (m_path.empty()) {
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(
193 ctx, reinterpret_cast<unsigned char *>(decbuff.data()), &outlen1, reinterpret_cast<unsigned char *>(rawbuff.data()), size)
195 || EVP_DecryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(decbuff.data()) + outlen1, &outlen2) != 1) {
197 EVP_CIPHER_CTX_free(ctx);
200 unsigned long errorCode = ERR_get_error();
201 while (errorCode != 0) {
205 msg += ERR_error_string(errorCode, 0);
206 errorCode = ERR_get_error();
211 EVP_CIPHER_CTX_free(ctx);
213 size = outlen1 + outlen2;
216 decbuff.swap(rawbuff);
219 if (compressionUsed) {
223 uLongf decompressedSize = ConversionUtilities::LE::toUInt64(decbuff.data());
224 rawbuff.resize(decompressedSize);
225 switch (uncompress(reinterpret_cast<Bytef *>(rawbuff.data()), &decompressedSize, reinterpret_cast<Bytef *>(decbuff.data() + 8),
226 size - static_cast<fstream::pos_type>(8))) {
228 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
230 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
232 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
234 decbuff.swap(rawbuff);
235 size = decompressedSize;
239 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
240 buffstr.write(decbuff.data(),
static_cast<streamsize
>(
size));
242 buffstr.seekg(0, ios_base::beg);
243 if (version >= 0x5u) {
244 uint16 extendedHeaderSize = m_freader.readUInt16BE();
245 m_encryptedExtendedHeader = m_freader.readString(extendedHeaderSize);
247 m_rootEntry.reset(
new NodeEntry(buffstr));
261 throw runtime_error(
"Root entry has not been created.");
264 if (m_file.is_open()) {
268 m_file.open(m_path, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);
270 m_fwriter.writeUInt32LE(0x7770616DU);
272 m_fwriter.writeUInt32LE(m_extendedHeader.empty() && m_encryptedExtendedHeader.empty() ? 0x3U : (m_encryptedExtendedHeader.empty() ? 0x4U : 0x5U));
275 flags |= 0x80 | 0x40;
277 if (useCompression) {
280 m_fwriter.writeByte(flags);
282 if (!m_extendedHeader.empty()) {
283 m_fwriter.writeUInt16BE(m_extendedHeader.size());
284 m_fwriter.writeString(m_extendedHeader);
287 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
288 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
290 if (!m_encryptedExtendedHeader.empty()) {
291 m_fwriter.writeUInt16BE(m_encryptedExtendedHeader.size());
292 m_fwriter.writeString(m_encryptedExtendedHeader);
294 m_rootEntry->make(buffstr);
295 buffstr.seekp(0, ios_base::end);
296 stringstream::pos_type
size = buffstr.tellp();
299 vector<char> decbuff(size, 0);
300 buffstr.read(decbuff.data(),
size);
301 vector<char> encbuff;
303 if (useCompression) {
304 uLongf compressedSize = compressBound(size);
305 encbuff.resize(8 + compressedSize);
306 ConversionUtilities::LE::getBytes(static_cast<uint64>(size), encbuff.data());
307 switch (compress(reinterpret_cast<Bytef *>(encbuff.data() + 8), &compressedSize, reinterpret_cast<Bytef *>(decbuff.data()), size)) {
309 throw runtime_error(
"Decompressing failed. The source buffer was too small.");
311 throw runtime_error(
"Decompressing failed. The destination buffer was too small.");
313 encbuff.swap(decbuff);
314 size = 8 + compressedSize;
320 EVP_CIPHER_CTX *ctx =
nullptr;
322 int outlen1, outlen2;
323 encbuff.resize(size + static_cast<fstream::pos_type>(32));
324 if (RAND_bytes(iv, aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr 325 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr, reinterpret_cast<unsigned const char *>(m_password), iv) != 1
326 || EVP_EncryptUpdate(
327 ctx, reinterpret_cast<unsigned char *>(encbuff.data()), &outlen1, reinterpret_cast<unsigned char *>(decbuff.data()), size)
329 || EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(encbuff.data()) + outlen1, &outlen2) != 1) {
331 EVP_CIPHER_CTX_free(ctx);
334 unsigned long errorCode = ERR_get_error();
335 while (errorCode != 0) {
339 msg += ERR_error_string(errorCode, 0);
340 errorCode = ERR_get_error();
345 EVP_CIPHER_CTX_free(ctx);
348 m_file.write(reinterpret_cast<char *>(iv), aes256cbcIvSize);
349 m_file.write(encbuff.data(),
static_cast<streamsize
>(outlen1 + outlen2));
353 m_file.write(decbuff.data(),
static_cast<streamsize
>(
size));
375 m_extendedHeader.clear();
376 m_encryptedExtendedHeader.clear();
388 throw runtime_error(
"Root entry has not been created.");
390 fstream output(targetPath.c_str(), ios_base::out);
391 function<void(int level)> indention = [&output](
int level) {
392 for (
int i = 0; i < level; ++i) {
396 function<void(const Entry *entry, int level)> printNode;
397 printNode = [&output, &printNode, &indention](
const Entry *entry,
int level) {
399 output <<
" - " << entry->label() << endl;
400 switch (entry->type()) {
402 for (
const Entry *child : static_cast<const NodeEntry *>(entry)->children()) {
403 printNode(child, level + 1);
407 for (
const Field &field : static_cast<const AccountEntry *>(entry)->fields()) {
409 output <<
" " << field.name();
410 for (
int i = field.name().length(); i < 15; ++i) {
413 output << field.value() << endl;
417 printNode(m_rootEntry.get(), 0);
430 m_file.seekg(0, ios_base::end);
431 if (m_file.tellg()) {
433 fstream backupFile(m_path +
".backup", ios::out | ios::trunc | ios::binary);
434 backupFile.exceptions(ios_base::failbit | ios_base::badbit);
435 backupFile << m_file.rdbuf();
449 return m_rootEntry !=
nullptr;
457 return m_rootEntry.get();
465 return m_rootEntry.get();
473 if (m_file.is_open()) {
519 value.copy(m_password, 32, 0);
527 memset(m_password, 0, 32);
540 if (m_freader.readUInt32LE() != 0x7770616DU) {
544 uint32 version = m_freader.readUInt32LE();
545 if (version == 0x1U || version == 0x2U) {
547 }
else if (version == 0x3U) {
548 return m_freader.readByte() & 0x80;
559 return m_file.is_open();
570 m_file.seekg(0, ios::end);
571 return m_file.tellg();
const std::string & path() const
Returns the current file path.
PasswordFile()
Constructs a new password file.
bool hasRootEntry() const
Returns an indication whether a root entry is present.
void clear()
Closes the file if opened.
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...
void load()
Reads the contents of the file.
void open(bool readOnly=false)
Opens the file.
void close()
Closes the file if currently opened.
void doBackup()
Creates a backup of the file.
Contains all IO related classes.
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
void exportToTextfile(const std::string &targetPath) const
Writes the current root entry to a plain text file.
bool isOpen() const
Returns an indication whether the file is open.
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.
bool isEncryptionUsed()
Returns an indication whether encryption is used if the file is open; returns always false otherwise...
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.
const char * password() const
Returns the current password.
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...