less flushing
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
|||||||
/build
|
/build
|
||||||
*.py
|
*.py
|
||||||
*.dull
|
*.dull
|
||||||
|
/TODO
|
||||||
|
/.clangd
|
||||||
|
|||||||
11
src/crypto.h
11
src/crypto.h
@@ -1,11 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <botan/aead.h>
|
#include <botan/aead.h>
|
||||||
#include <botan/auto_rng.h>
|
#include <botan/auto_rng.h>
|
||||||
#include <botan/pwdhash.h>
|
#include <botan/pwdhash.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
|
|
||||||
inline Botan::secure_vector<u8> encrypt(const Botan::secure_vector<u8> &key,
|
inline Botan::secure_vector<u8> encrypt(const Botan::secure_vector<u8> &key,
|
||||||
@@ -16,15 +15,16 @@ inline Botan::secure_vector<u8> encrypt(const Botan::secure_vector<u8> &key,
|
|||||||
|
|
||||||
// XChaCha20 is selected automatically based on the nonce size
|
// XChaCha20 is selected automatically based on the nonce size
|
||||||
// https://github.com/randombit/botan/blob/master/src/lib/stream/chacha/chacha.cpp#L375
|
// https://github.com/randombit/botan/blob/master/src/lib/stream/chacha/chacha.cpp#L375
|
||||||
static auto cipher = Botan::AEAD_Mode::create_or_throw(
|
static thread_local auto cipher = Botan::AEAD_Mode::create_or_throw(
|
||||||
"ChaCha20Poly1305", Botan::Cipher_Dir::Encryption);
|
"ChaCha20Poly1305", Botan::Cipher_Dir::Encryption);
|
||||||
|
cipher->clear();
|
||||||
|
|
||||||
auto nonce = rng.random_array<24>();
|
auto nonce = rng.random_array<24>();
|
||||||
cipher->set_key(key);
|
cipher->set_key(key);
|
||||||
cipher->start(nonce);
|
cipher->start(nonce);
|
||||||
|
|
||||||
Botan::secure_vector<u8> res;
|
Botan::secure_vector<u8> res;
|
||||||
res.resize(24 + plaintext.size() + 16);
|
res.resize(24 + plaintext.size());
|
||||||
std::ranges::copy(nonce, res.begin());
|
std::ranges::copy(nonce, res.begin());
|
||||||
std::ranges::copy(plaintext, res.begin() + 24);
|
std::ranges::copy(plaintext, res.begin() + 24);
|
||||||
cipher->finish(res, 24);
|
cipher->finish(res, 24);
|
||||||
@@ -40,8 +40,9 @@ decrypt(const Botan::secure_vector<u8> &key,
|
|||||||
std::array<u8, 24> nonce{};
|
std::array<u8, 24> nonce{};
|
||||||
std::copy_n(ciphertext_with_nonce.begin(), 24, nonce.begin());
|
std::copy_n(ciphertext_with_nonce.begin(), 24, nonce.begin());
|
||||||
|
|
||||||
static auto cipher = Botan::AEAD_Mode::create_or_throw(
|
static thread_local auto cipher = Botan::AEAD_Mode::create_or_throw(
|
||||||
"ChaCha20Poly1305", Botan::Cipher_Dir::Decryption);
|
"ChaCha20Poly1305", Botan::Cipher_Dir::Decryption);
|
||||||
|
cipher->clear();
|
||||||
|
|
||||||
cipher->set_key(key);
|
cipher->set_key(key);
|
||||||
cipher->start(nonce);
|
cipher->start(nonce);
|
||||||
|
|||||||
@@ -70,10 +70,9 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
ui->statusbar->showMessage("Creating the vault...");
|
ui->statusbar->showMessage("Creating the vault...");
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
Vault::create(path.toStdString(), password.toStdString());
|
|
||||||
|
|
||||||
m_vault =
|
m_vault =
|
||||||
std::make_unique<Vault>(path.toStdString(), password.toStdString());
|
Vault::create_and_open(path.toStdString(), password.toStdString());
|
||||||
|
|
||||||
ui->statusbar->showMessage("Opened " + path);
|
ui->statusbar->showMessage("Opened " + path);
|
||||||
|
|
||||||
reload_fs_tree();
|
reload_fs_tree();
|
||||||
@@ -134,6 +133,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
file.read(content.data(), size);
|
file.read(content.data(), size);
|
||||||
m_vault->create_file(basename(path.toStdString()), content);
|
m_vault->create_file(basename(path.toStdString()), content);
|
||||||
}
|
}
|
||||||
|
m_vault->flush();
|
||||||
|
|
||||||
reload_fs_tree();
|
reload_fs_tree();
|
||||||
ui->statusbar->showMessage("Added " + QString::number(paths.size()) +
|
ui->statusbar->showMessage("Added " + QString::number(paths.size()) +
|
||||||
@@ -304,6 +304,7 @@ void MainWindow::file_context_menu(const QPoint &pos) {
|
|||||||
style()->standardIcon(QStyle::SP_DialogCancelButton), "Delete");
|
style()->standardIcon(QStyle::SP_DialogCancelButton), "Delete");
|
||||||
connect(delete_action, &QAction::triggered, this, [this, item]() {
|
connect(delete_action, &QAction::triggered, this, [this, item]() {
|
||||||
m_vault->delete_file(item->text(0).toStdString());
|
m_vault->delete_file(item->text(0).toStdString());
|
||||||
|
m_vault->flush();
|
||||||
reload_fs_tree();
|
reload_fs_tree();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -346,6 +347,7 @@ void MainWindow::dropEvent(QDropEvent *event) {
|
|||||||
file.read(content.data(), size);
|
file.read(content.data(), size);
|
||||||
m_vault->create_file(basename(u.toLocalFile().toStdString()), content);
|
m_vault->create_file(basename(u.toLocalFile().toStdString()), content);
|
||||||
}
|
}
|
||||||
|
m_vault->flush();
|
||||||
ui->statusbar->showMessage(
|
ui->statusbar->showMessage(
|
||||||
"Added " + QString::number(event->mimeData()->urls().size()) + " files");
|
"Added " + QString::number(event->mimeData()->urls().size()) + " files");
|
||||||
reload_fs_tree();
|
reload_fs_tree();
|
||||||
|
|||||||
36
src/vault.cc
36
src/vault.cc
@@ -49,7 +49,8 @@ Vault::Vault(std::string path, const std::string &password)
|
|||||||
build_file_map();
|
build_file_map();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vault::create(const std::string &path, const std::string &password) {
|
std::unique_ptr<Vault> Vault::create_and_open(const std::string &path,
|
||||||
|
const std::string &password) {
|
||||||
static Botan::AutoSeeded_RNG rng;
|
static Botan::AutoSeeded_RNG rng;
|
||||||
auto salt = rng.random_array<16>();
|
auto salt = rng.random_array<16>();
|
||||||
|
|
||||||
@@ -64,6 +65,8 @@ void Vault::create(const std::string &path, const std::string &password) {
|
|||||||
WRITE(file, to_char_ptr(salt.data()), 16);
|
WRITE(file, to_char_ptr(salt.data()), 16);
|
||||||
WRITE(file, to_char_ptr(check_ciphertext.data()), 46);
|
WRITE(file, to_char_ptr(check_ciphertext.data()), 46);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
return std::make_unique<Vault>(path, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vault::build_file_map() {
|
void Vault::build_file_map() {
|
||||||
@@ -86,8 +89,8 @@ void Vault::build_file_map() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Vault::read_file(const std::string &filename) {
|
std::string Vault::read_file(const std::string &name) {
|
||||||
auto header = m_file_map.at(filename);
|
auto header = m_file_map.at(name);
|
||||||
ASSERT(!header.deleted);
|
ASSERT(!header.deleted);
|
||||||
|
|
||||||
m_file.clear();
|
m_file.clear();
|
||||||
@@ -102,14 +105,15 @@ std::string Vault::read_file(const std::string &filename) {
|
|||||||
return {to_char_ptr(plaintext.data()), plaintext.size()};
|
return {to_char_ptr(plaintext.data()), plaintext.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vault::create_file(const std::string &filename,
|
void Vault::create_file(const std::string &name, const std::string &content) {
|
||||||
const std::string &content) {
|
ASSERT(!m_file_map.contains(name));
|
||||||
|
|
||||||
m_file.clear();
|
m_file.clear();
|
||||||
m_file.seekp(0, std::ios::end);
|
m_file.seekp(0, std::ios::end);
|
||||||
|
|
||||||
auto offset = m_file.tellp();
|
auto offset = m_file.tellp();
|
||||||
|
|
||||||
auto name_ciphertext = Crypto::encrypt(m_key, to_span(filename));
|
auto name_ciphertext = Crypto::encrypt(m_key, to_span(name));
|
||||||
u64 name_ciphertext_size = name_ciphertext.size();
|
u64 name_ciphertext_size = name_ciphertext.size();
|
||||||
|
|
||||||
auto ciphertext = Crypto::encrypt(m_key, to_span(content));
|
auto ciphertext = Crypto::encrypt(m_key, to_span(content));
|
||||||
@@ -122,20 +126,19 @@ void Vault::create_file(const std::string &filename,
|
|||||||
WRITE(m_file, &ciphertext_size, sizeof(u64));
|
WRITE(m_file, &ciphertext_size, sizeof(u64));
|
||||||
auto content_offset = m_file.tellp();
|
auto content_offset = m_file.tellp();
|
||||||
WRITE(m_file, ciphertext.data(), ciphertext_size);
|
WRITE(m_file, ciphertext.data(), ciphertext_size);
|
||||||
m_file.flush();
|
|
||||||
|
|
||||||
m_file_map[filename] = FileHeader{
|
m_file_map[name] = FileHeader{
|
||||||
.offset = static_cast<u64>(offset),
|
.offset = static_cast<u64>(offset),
|
||||||
.deleted = false,
|
.deleted = false,
|
||||||
.name_ciphertext_size = name_ciphertext_size,
|
.name_ciphertext_size = name_ciphertext_size,
|
||||||
.name = filename,
|
.name = name,
|
||||||
.content_ciphertext_size = ciphertext_size,
|
.content_ciphertext_size = ciphertext_size,
|
||||||
.content_offset = static_cast<u64>(content_offset),
|
.content_offset = static_cast<u64>(content_offset),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vault::delete_file(const std::string &filename) {
|
void Vault::delete_file(const std::string &name) {
|
||||||
auto header = m_file_map.at(filename);
|
auto header = m_file_map.at(name);
|
||||||
|
|
||||||
// zero the ciphertext and mark as deleted
|
// zero the ciphertext and mark as deleted
|
||||||
m_file.clear();
|
m_file.clear();
|
||||||
@@ -152,15 +155,14 @@ void Vault::delete_file(const std::string &filename) {
|
|||||||
m_file.seekp(static_cast<i64>(header.offset), std::ios::beg);
|
m_file.seekp(static_cast<i64>(header.offset), std::ios::beg);
|
||||||
bool deleted = true;
|
bool deleted = true;
|
||||||
WRITE(m_file, &deleted, sizeof(bool));
|
WRITE(m_file, &deleted, sizeof(bool));
|
||||||
m_file.flush();
|
|
||||||
|
|
||||||
m_file_map.erase(filename);
|
m_file_map.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vault::update_file(const std::string &filename,
|
void Vault::update_file(const std::string &name, const std::string &content) {
|
||||||
const std::string &content) {
|
delete_file(name);
|
||||||
delete_file(filename);
|
create_file(name, content);
|
||||||
create_file(filename, content);
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<FileHeader> Vault::read_file_header() {
|
std::optional<FileHeader> Vault::read_file_header() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <botan/secmem.h>
|
#include <botan/secmem.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -22,12 +23,14 @@ class Vault {
|
|||||||
public:
|
public:
|
||||||
explicit Vault(std::string path, const std::string &password);
|
explicit Vault(std::string path, const std::string &password);
|
||||||
|
|
||||||
static void create(const std::string &path, const std::string &password);
|
static std::unique_ptr<Vault> create_and_open(const std::string &path,
|
||||||
|
const std::string &password);
|
||||||
|
|
||||||
std::string read_file(const std::string &name);
|
std::string read_file(const std::string &name);
|
||||||
void create_file(const std::string &name, const std::string &content);
|
void create_file(const std::string &name, const std::string &content);
|
||||||
void delete_file(const std::string &name);
|
void delete_file(const std::string &name);
|
||||||
void update_file(const std::string &name, const std::string &content);
|
void update_file(const std::string &name, const std::string &content);
|
||||||
|
void flush() { m_file.flush(); }
|
||||||
|
|
||||||
const std::string &path() const { return m_path; }
|
const std::string &path() const { return m_path; }
|
||||||
const std::unordered_map<std::string, FileHeader> &file_map() const {
|
const std::unordered_map<std::string, FileHeader> &file_map() const {
|
||||||
|
|||||||
Reference in New Issue
Block a user