diff --git a/README.md b/README.md index b63cb91..9217499 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Desktop app for securely storing sensitive files ## Features * **Pretty usable UI** -* **Overkill encryption:** XChaCha20-Poly1305 + Argon2id(m=1GB,t=8,p=4) key derivation -* **Cross-platform-ish:** Supports Linux and Windows +* **Overkill encryption:** XChaCha20-Poly1305 + Argon2id(m=1GB,t=6,p=4) key derivation +* **Cross-platform-ish:** Tested on Linux and Windows ## Building diff --git a/src/mainwindow.cc b/src/mainwindow.cc index c566c5f..01b0d3a 100644 --- a/src/mainwindow.cc +++ b/src/mainwindow.cc @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -97,8 +97,8 @@ void MainWindow::reload_fs_tree() { for (const auto &header : headers) { auto *item = new QTreeWidgetItem(ui->fsTreeWidget); item->setText(0, QString::fromStdString(header.name)); - item->setText(1, QString::number(header.size)); - item->setText(2, QString::number(header.offset)); + item->setText(1, QString::number(header.content_size)); + item->setText(2, QString::number(header.global_offset)); item->setText(3, QString::fromStdString(Botan::hex_encode(header.nonce))); } for (int i = 0; i < ui->fsTreeWidget->columnCount(); ++i) { @@ -120,25 +120,29 @@ void MainWindow::preview_file(const std::string &filename) { void MainWindow::edit_file(const std::string &filename) { auto content = m_vault->read_file(filename); if (content) { - QTemporaryFile temp_file; - ASSERT(temp_file.open()); - { - QTextStream out(&temp_file); - out << QString::fromStdString(content.value()); - } - temp_file.flush(); + QTemporaryDir dir; + ASSERT(dir.isValid()); - QDesktopServices::openUrl(QUrl::fromLocalFile(temp_file.fileName())); + std::string path = dir.path().toStdString() + "/" + filename; + + std::ofstream file(path); + file.write(content->data(), static_cast(content->size())); + file.close(); + + QDesktopServices::openUrl( + QUrl::fromLocalFile(QString::fromStdString(path))); QMessageBox::information(this, "Edit", "Please edit the file in the opened editor and " "save it. Click OK when done."); - temp_file.seek(0); - QTextStream in(&temp_file); - m_vault->update_file(filename, in.readAll().toStdString()); + std::ifstream file2(path, std::ios::binary); + std::string new_content((std::istreambuf_iterator(file2)), + std::istreambuf_iterator()); + + m_vault->update_file(filename, new_content); reload_fs_tree(); - // QTemporaryFile gets deleted when it goes out of scope + // QTemporaryDir gets deleted when it goes out of scope } else { qWarning() << "File to edit not found"; } diff --git a/src/vault.cc b/src/vault.cc index 2d363c5..96d4c7a 100644 --- a/src/vault.cc +++ b/src/vault.cc @@ -32,10 +32,10 @@ std::vector Vault::read_file_headers() { m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg); while (true) { - auto header = read_file_header(m_file); + auto header = read_file_header(); if (header) { headers.push_back(header.value()); - m_file.seekg(static_cast(header->size), std::ios::cur); + m_file.seekg(static_cast(header->content_size), std::ios::cur); } else { break; } @@ -49,16 +49,16 @@ std::optional Vault::read_file(const std::string &filename) { m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg); while (true) { - auto header = read_file_header(m_file); + auto header = read_file_header(); if (!header) { break; } if (header->name == filename) { Botan::secure_vector ciphertext; - ciphertext.resize(header->size); + ciphertext.resize(header->content_size); if (!m_file.read(to_char_ptr(ciphertext.data()), - static_cast(header->size))) { + static_cast(header->content_size))) { break; } @@ -67,7 +67,7 @@ std::optional Vault::read_file(const std::string &filename) { return std::string(to_char_ptr(plaintext.data()), plaintext.size()); } - m_file.seekg(static_cast(header->size), std::ios::cur); + m_file.seekg(static_cast(header->content_size), std::ios::cur); } return std::nullopt; @@ -78,22 +78,24 @@ void Vault::create_file(const std::string &filename, m_file.clear(); m_file.seekp(0, std::ios::end); - u64 filename_size = filename.size(); - - Botan::secure_vector plaintext(content.begin(), content.end()); - static Botan::AutoSeeded_RNG rng; auto nonce_sv = rng.random_vec(24); std::vector nonce(nonce_sv.begin(), nonce_sv.end()); - auto ciphertext = Crypto::encrypt_xchacha20_poly1305(plaintext, m_key, nonce); + Botan::secure_vector filename_sv(filename.begin(), filename.end()); + auto filename_ciphertext = + Crypto::encrypt_xchacha20_poly1305(filename_sv, m_key, nonce); + u64 filename_ciphertext_size = filename_ciphertext.size(); + + Botan::secure_vector content_sv(content.begin(), content.end()); + auto ciphertext = + Crypto::encrypt_xchacha20_poly1305(content_sv, m_key, nonce); u64 ciphertext_size = ciphertext.size(); - // TODO: encrypt filenames as well - - ASSERT(m_file.write(to_char_ptr(&filename_size), sizeof(u64))); - ASSERT(m_file.write(filename.data(), static_cast(filename_size))); ASSERT(m_file.write(to_char_ptr(nonce.data()), nonce.size())); + ASSERT(m_file.write(to_char_ptr(&filename_ciphertext_size), sizeof(u64))); + ASSERT(m_file.write(to_char_ptr(filename_ciphertext.data()), + static_cast(filename_ciphertext_size))); ASSERT(m_file.write(to_char_ptr(&ciphertext_size), sizeof(u64))); ASSERT(m_file.write(to_char_ptr(ciphertext.data()), static_cast(ciphertext_size))); @@ -110,20 +112,20 @@ void Vault::delete_file(const std::string &filename) { while (true) { i64 current_pos = m_file.tellg(); - auto header = read_file_header(m_file); + auto header = read_file_header(); if (!header) { break; } if (header->name == filename) { entry_start = current_pos; - entry_total_size = - sizeof(u64) + header->name.length() + 24 + sizeof(u64) + header->size; - m_file.seekg(static_cast(header->size), std::ios::cur); + entry_total_size = 24 + sizeof(u64) + header->name_ciphertext_size + + sizeof(u64) + header->content_size; + m_file.seekg(static_cast(header->content_size), std::ios::cur); break; } - m_file.seekg(static_cast(header->size), std::ios::cur); + m_file.seekg(static_cast(header->content_size), std::ios::cur); } if (entry_start != -1) { @@ -156,29 +158,34 @@ void Vault::update_file(const std::string &filename, create_file(filename, content); } -std::optional Vault::read_file_header(std::fstream &file) { +std::optional Vault::read_file_header() { FileHeader header{}; - header.offset = file.tellg(); - - u64 name_size = 0; - if (!file.read(to_char_ptr(&name_size), sizeof(u64))) { - return std::nullopt; - } - - ASSERT(name_size < 10000); - - header.name.resize(name_size); - if (!file.read(header.name.data(), static_cast(name_size))) { - return std::nullopt; - } + header.global_offset = m_file.tellg(); header.nonce.resize(24); - if (!file.read(to_char_ptr(header.nonce.data()), 24)) { + if (!m_file.read(to_char_ptr(header.nonce.data()), 24)) { return std::nullopt; } - if (!file.read(to_char_ptr(&header.size), sizeof(u64))) { + if (!m_file.read(to_char_ptr(&header.name_ciphertext_size), sizeof(u64))) { + return std::nullopt; + } + + ASSERT(header.name_ciphertext_size < 10000); + + Botan::secure_vector name_ciphertext; + name_ciphertext.resize(header.name_ciphertext_size); + if (!m_file.read(to_char_ptr(name_ciphertext.data()), + static_cast(header.name_ciphertext_size))) { + return std::nullopt; + } + + auto name = + Crypto::decrypt_xchacha20_poly1305(name_ciphertext, m_key, header.nonce); + header.name = std::string(name.begin(), name.end()); + + if (!m_file.read(to_char_ptr(&header.content_size), sizeof(u64))) { return std::nullopt; } return header; -} +} \ No newline at end of file diff --git a/src/vault.h b/src/vault.h index c6a0726..317c520 100644 --- a/src/vault.h +++ b/src/vault.h @@ -11,9 +11,10 @@ constexpr u64 AFTER_HEADER_OFFSET = 22; struct FileHeader { std::string name; - u64 size; + u64 name_ciphertext_size; + u64 content_size; std::vector nonce; - u64 offset; + u64 global_offset; }; class Vault { @@ -26,10 +27,10 @@ public: void delete_file(const std::string &name); void update_file(const std::string &name, const std::string &content); - static std::optional read_file_header(std::fstream &file); - private: std::string m_path; std::fstream m_file; Botan::secure_vector m_key; + + std::optional read_file_header(); };