Moved some parts to a static lib so both the executable and the tests can link to it.
Written additional tests.
This commit is contained in:
parent
0a809a7288
commit
d0ea9dfa0c
39 changed files with 1767 additions and 493 deletions
6
core/Core.cpp
Normal file
6
core/Core.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#include "Core.h"
|
||||
|
||||
|
||||
Core::Core()
|
||||
{
|
||||
}
|
||||
12
core/Core.h
Normal file
12
core/Core.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef CORE_H
|
||||
#define CORE_H
|
||||
|
||||
|
||||
class Core
|
||||
{
|
||||
|
||||
public:
|
||||
Core();
|
||||
};
|
||||
|
||||
#endif // CORE_H
|
||||
64
core/CsvWriter.cpp
Normal file
64
core/CsvWriter.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "csvwriter.h"
|
||||
|
||||
CsvWriter::CsvWriter()
|
||||
{}
|
||||
|
||||
CsvWriter::CsvWriter(QTextStream *output)
|
||||
: m_output(output)
|
||||
{}
|
||||
|
||||
void CsvWriter::setDestination(QTextStream *output)
|
||||
{
|
||||
m_output = output;
|
||||
m_column = 0;
|
||||
}
|
||||
|
||||
void CsvWriter::setSeperator(QChar ch)
|
||||
{
|
||||
m_seperator = ch;
|
||||
}
|
||||
|
||||
void CsvWriter::setQuote(QChar ch)
|
||||
{
|
||||
m_quote = ch;
|
||||
}
|
||||
|
||||
void CsvWriter::writeField(QString field)
|
||||
{
|
||||
QTextStream &out = *m_output;
|
||||
if (m_column > 0) {
|
||||
out << m_seperator;
|
||||
}
|
||||
// if field contains any of seperator, quote or newline then it needs to be quoted
|
||||
// when quoted quotes need to be doubled to escape them
|
||||
bool needs_quotes = false;
|
||||
for (auto ch : field) {
|
||||
if (ch == '\n' || ch == m_seperator || ch == m_quote) {
|
||||
needs_quotes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_quotes) {
|
||||
out << m_quote;
|
||||
for (auto ch : field) {
|
||||
if (ch == m_quote)
|
||||
out << m_quote;
|
||||
out << ch;
|
||||
}
|
||||
out << m_quote;
|
||||
}
|
||||
else {
|
||||
out << field;
|
||||
}
|
||||
++m_column;
|
||||
}
|
||||
|
||||
|
||||
void CsvWriter::nextRow()
|
||||
{
|
||||
QTextStream &out = *m_output;
|
||||
out << '\n';
|
||||
m_column = 0;
|
||||
}
|
||||
|
||||
23
core/CsvWriter.h
Normal file
23
core/CsvWriter.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef CSVWRITER_H
|
||||
#define CSVWRITER_H
|
||||
|
||||
#include <ostream>
|
||||
#include <QTextStream>
|
||||
|
||||
class CsvWriter {
|
||||
public:
|
||||
CsvWriter();
|
||||
explicit CsvWriter(QTextStream *output);
|
||||
void setDestination(QTextStream *output);
|
||||
void setSeperator(QChar ch);
|
||||
void setQuote(QChar ch);
|
||||
void writeField(QString field);
|
||||
void nextRow();
|
||||
private:
|
||||
QChar m_seperator = ',';
|
||||
QChar m_quote = '"';
|
||||
QTextStream *m_output = nullptr;
|
||||
int m_column = 0;
|
||||
};
|
||||
|
||||
#endif // CSVWRITER_H
|
||||
269
core/Expected.h
Normal file
269
core/Expected.h
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
#ifndef EXPECTED_H
|
||||
#define EXPECTED_H
|
||||
|
||||
#include <exception>
|
||||
|
||||
template <typename T>
|
||||
class Expected {
|
||||
union {
|
||||
T m_value;
|
||||
std::exception_ptr m_error;
|
||||
};
|
||||
bool m_valid;
|
||||
Expected() {} // internal use
|
||||
|
||||
public:
|
||||
|
||||
Expected(const T& rhs)
|
||||
: m_value(rhs), m_valid(true)
|
||||
{}
|
||||
|
||||
Expected(T&& rhs)
|
||||
: m_value(std::move(rhs))
|
||||
, m_valid(true)
|
||||
{}
|
||||
|
||||
|
||||
Expected(const Expected& rhs)
|
||||
: m_valid(rhs.valid)
|
||||
{
|
||||
if (m_valid) {
|
||||
new (&m_value) T(rhs.m_value);
|
||||
}
|
||||
else {
|
||||
new (&m_error) std::exception_ptr(rhs.m_error);
|
||||
}
|
||||
}
|
||||
|
||||
Expected(Expected &&rhs)
|
||||
: m_valid(rhs.m_valid)
|
||||
{
|
||||
if (m_valid) {
|
||||
new (&m_value) T(std::move(rhs.m_value));
|
||||
}
|
||||
else {
|
||||
new (&m_error) std::exception_ptr(std::move(rhs.m_error));
|
||||
}
|
||||
}
|
||||
|
||||
~Expected()
|
||||
{
|
||||
if (m_valid) {
|
||||
m_value.~T();
|
||||
}
|
||||
else {
|
||||
using std::exception_ptr;
|
||||
m_error.~exception_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
// void swap(Expected& rhs)
|
||||
// {
|
||||
// if (m_valid) {
|
||||
// if (rhs.m_valid) {
|
||||
// using std::swap;
|
||||
// swap(m_value, rhs.m_value);
|
||||
// }
|
||||
// else {
|
||||
// auto t = std::move(rhs.m_error);
|
||||
// new(&rhs.m_value) T(std::move(m_value));
|
||||
// new(&m_error) std::exception_ptr(t);
|
||||
// std::swap(m_valid, rhs.m_valid);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// if (rhs.m_valid) {
|
||||
// rhs.swap(*this);
|
||||
// }
|
||||
// else {
|
||||
// using std::swap;
|
||||
// swap(m_error, rhs.m_error);
|
||||
// std::swap(m_valid, rhs.m_valid);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
template <class E>
|
||||
static Expected<T> fromException(const E& exception)
|
||||
{
|
||||
if (typeid(exception) != typeid(E)) {
|
||||
throw std::invalid_argument("slicing detected");
|
||||
}
|
||||
return fromException(std::make_exception_ptr(exception));
|
||||
}
|
||||
|
||||
static Expected<T> fromException(std::exception_ptr p)
|
||||
{
|
||||
Expected<T> result;
|
||||
result.m_valid = false;
|
||||
new (&result.m_error) std::exception_ptr(std::move(p));
|
||||
return result;
|
||||
}
|
||||
|
||||
static Expected<T> fromException()
|
||||
{
|
||||
return fromException(std::current_exception());
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
T& get()
|
||||
{
|
||||
if (!m_valid) {
|
||||
std::rethrow_exception(m_error);
|
||||
}
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
if (!m_valid) {
|
||||
std::rethrow_exception(m_error);
|
||||
}
|
||||
return m_value;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
bool hasException() const
|
||||
{
|
||||
try {
|
||||
if (!m_valid) {
|
||||
std::rethrow_exception(m_error);
|
||||
}
|
||||
}
|
||||
catch (const E& ) {
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static Expected fromCode(F fun)
|
||||
{
|
||||
try {
|
||||
return Expected(fun());
|
||||
}
|
||||
catch (...) {
|
||||
return fromException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
class Expected<void> {
|
||||
std::exception_ptr m_error;
|
||||
bool m_valid;
|
||||
|
||||
public:
|
||||
|
||||
Expected<void>()
|
||||
: m_valid(true)
|
||||
{}
|
||||
|
||||
|
||||
Expected(const Expected& rhs)
|
||||
: m_valid(rhs.m_valid)
|
||||
, m_error(rhs.m_error)
|
||||
{}
|
||||
|
||||
Expected(Expected<void> &&rhs)
|
||||
: m_valid(rhs.m_valid)
|
||||
, m_error(std::move(rhs.m_error))
|
||||
{}
|
||||
|
||||
~Expected()
|
||||
{}
|
||||
|
||||
// void swap(Expected& rhs)
|
||||
// {
|
||||
// if (m_valid) {
|
||||
// if (!rhs.m_valid) {
|
||||
// m_error = rhs.m_error;
|
||||
// std::swap(m_valid, rhs.m_valid);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// if (rhs.m_valid) {
|
||||
// rhs.swap(*this);
|
||||
// }
|
||||
// else {
|
||||
// using std::swap;
|
||||
// swap(m_error, rhs.m_error);
|
||||
// std::swap(m_valid, rhs.m_valid);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
template <class E>
|
||||
static Expected<void> fromException(const E& exception)
|
||||
{
|
||||
if (typeid(exception) != typeid(E)) {
|
||||
throw std::invalid_argument("slicing detected");
|
||||
}
|
||||
return fromException(std::make_exception_ptr(exception));
|
||||
}
|
||||
|
||||
static Expected<void> fromException(std::exception_ptr p)
|
||||
{
|
||||
Expected<void> result;
|
||||
result.m_valid = false;
|
||||
result.m_error = std::exception_ptr(std::move(p));
|
||||
return result;
|
||||
}
|
||||
|
||||
static Expected<void> fromException()
|
||||
{
|
||||
return fromException(std::current_exception());
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
void get() const
|
||||
{
|
||||
if (!m_valid) {
|
||||
std::rethrow_exception(m_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class E>
|
||||
bool hasException() const
|
||||
{
|
||||
try {
|
||||
if (!m_valid) {
|
||||
std::rethrow_exception(m_error);
|
||||
}
|
||||
}
|
||||
catch (const E& ) {
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static Expected fromCode(F fun)
|
||||
{
|
||||
try {
|
||||
fun();
|
||||
return Expected<void>();
|
||||
}
|
||||
catch (...) {
|
||||
return fromException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // EXPECTED_H
|
||||
244
core/PasswordManager.cpp
Normal file
244
core/PasswordManager.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#include "PasswordManager.h"
|
||||
|
||||
#include <botan/filters.h>
|
||||
#include <botan/pipe.h>
|
||||
#include <botan/sha2_64.h>
|
||||
#include <botan/hash.h>
|
||||
#include <botan/hmac.h>
|
||||
#include <botan/pbkdf2.h>
|
||||
#include <botan/rng.h>
|
||||
#include <botan/base64.h>
|
||||
#include <botan/loadstor.h>
|
||||
#include <botan/mem_ops.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include "Core.h"
|
||||
|
||||
using namespace Botan;
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
|
||||
for later use as flags, etc if needed
|
||||
*/
|
||||
const uint8_t c_PasswordVersionCode = 0x10;
|
||||
|
||||
const size_t c_VersionCodeLen = 1;
|
||||
|
||||
const size_t CIPHER_KEY_LEN = 32;
|
||||
const size_t CIPHER_IV_LEN = 16;
|
||||
const size_t MAC_KEY_LEN = 32;
|
||||
const size_t MAC_OUTPUT_LEN = 20;
|
||||
const size_t PBKDF_SALT_LEN = 10;
|
||||
const size_t PBKDF_ITERATIONS = 8 * 1024;
|
||||
|
||||
const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN;
|
||||
|
||||
const char * const c_Cipher = "Serpent/CTR-BE";
|
||||
|
||||
const char * const c_IniGroupSecurity = "Security";
|
||||
|
||||
|
||||
|
||||
StrengthenedKey generateKey(const std::string &passphrase, const uint8_t *salt, int saltlength)
|
||||
{
|
||||
PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
|
||||
OctetString master_key = pbkdf.derive_key(
|
||||
PBKDF_OUTPUT_LEN,
|
||||
passphrase,
|
||||
salt, saltlength,
|
||||
PBKDF_ITERATIONS);
|
||||
|
||||
const uint8_t* mk = master_key.begin();
|
||||
|
||||
return StrengthenedKey(
|
||||
SymmetricKey(mk, CIPHER_KEY_LEN),
|
||||
SymmetricKey(mk + CIPHER_KEY_LEN, MAC_KEY_LEN),
|
||||
InitializationVector(mk + CIPHER_KEY_LEN + MAC_KEY_LEN, CIPHER_IV_LEN));
|
||||
}
|
||||
|
||||
// secure_vector<uint8_t> pbkdf_salt(PBKDF_SALT_LEN);
|
||||
// rng.randomize( pbkdf_salt.data(), pbkdf_salt.size());
|
||||
// StrengthenedKey strengthened_key = generateKey(passphrase, pbkdf_salt.data(), pbkdf_salt.size());
|
||||
|
||||
|
||||
std::string encrypt(const std::string &input,
|
||||
const StrengthenedKey &strengthened_key)
|
||||
{
|
||||
|
||||
Pipe pipe(get_cipher(c_Cipher, strengthened_key.cipher_key,
|
||||
strengthened_key.iv, ENCRYPTION),
|
||||
new Fork(
|
||||
nullptr,
|
||||
new MAC_Filter(new HMAC(new SHA_512),
|
||||
strengthened_key.mac_key, MAC_OUTPUT_LEN)));
|
||||
|
||||
pipe.process_msg((const uint8_t*)input.data(), input.length());
|
||||
|
||||
/*
|
||||
Output format is:
|
||||
mac (20 bytes)
|
||||
ciphertext
|
||||
*/
|
||||
const size_t ciphertext_len = pipe.remaining(0);
|
||||
std::vector<uint8_t> out_buf(MAC_OUTPUT_LEN + ciphertext_len);
|
||||
|
||||
BOTAN_ASSERT_EQUAL(
|
||||
pipe.read(&out_buf[0], MAC_OUTPUT_LEN, 1),
|
||||
MAC_OUTPUT_LEN, "MAC output");
|
||||
BOTAN_ASSERT_EQUAL(
|
||||
pipe.read(&out_buf[MAC_OUTPUT_LEN], ciphertext_len, 0),
|
||||
ciphertext_len, "Ciphertext size");
|
||||
|
||||
return base64_encode(out_buf.data(), out_buf.size());
|
||||
}
|
||||
|
||||
std::string decrypt(const std::string &input, const StrengthenedKey &strengthened_key)
|
||||
{
|
||||
secure_vector<uint8_t> ciphertext = base64_decode(input);
|
||||
|
||||
if(ciphertext.size() < (MAC_OUTPUT_LEN)) {
|
||||
throw Decoding_Error("Invalid encrypted password input");
|
||||
}
|
||||
|
||||
Pipe pipe(new Fork(
|
||||
get_cipher(c_Cipher, strengthened_key.cipher_key, strengthened_key.iv, DECRYPTION),
|
||||
new MAC_Filter(new HMAC(new SHA_512), strengthened_key.mac_key, MAC_OUTPUT_LEN)
|
||||
));
|
||||
|
||||
const size_t ciphertext_offset = MAC_OUTPUT_LEN;
|
||||
pipe.process_msg(&ciphertext[ciphertext_offset], ciphertext.size() - ciphertext_offset);
|
||||
|
||||
uint8_t computed_mac[MAC_OUTPUT_LEN];
|
||||
BOTAN_ASSERT_EQUAL(MAC_OUTPUT_LEN, pipe.read(computed_mac, MAC_OUTPUT_LEN, 1), "MAC size");
|
||||
|
||||
if(!same_mem(computed_mac, &ciphertext[0], MAC_OUTPUT_LEN)) {
|
||||
throw Decoding_Error("Encrypted password integrity failure");
|
||||
}
|
||||
|
||||
return pipe.read_all_as_string(0);
|
||||
}
|
||||
|
||||
struct constants {
|
||||
const int pbkdf_salt_len;
|
||||
};
|
||||
|
||||
constants v1_consts = {
|
||||
10
|
||||
};
|
||||
|
||||
} // end of unnamed namespace
|
||||
|
||||
/*
|
||||
* File layout:
|
||||
*
|
||||
* Header
|
||||
* version
|
||||
* key_salt
|
||||
* hash_salt
|
||||
* master_hash
|
||||
*
|
||||
*
|
||||
* Passwords
|
||||
* key = pw
|
||||
*/
|
||||
|
||||
|
||||
|
||||
PasswordManager::PasswordManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Expected<bool> PasswordManager::unlock(const std::string &master_password)
|
||||
{
|
||||
try {
|
||||
bool result = false;
|
||||
if (m_masterHash.length() == 0 && master_password.empty()) {
|
||||
result = true;
|
||||
} else {
|
||||
StrengthenedKey key = generateKey(master_password, m_keySalt.begin(), m_keySalt.length());
|
||||
OctetString hash = hashStrengthenedKey(key, m_hashSalt);
|
||||
|
||||
BOOST_ASSERT_MSG(hash.length() == m_masterHash.length(), "Both hashes should have the same length! Versioning error?");
|
||||
|
||||
if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) {
|
||||
result = true;
|
||||
m_masterKey = key;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (...) {
|
||||
return Expected<bool>::fromException();
|
||||
}
|
||||
}
|
||||
|
||||
Expected<bool> PasswordManager::changeMasterPassword(const std::string &old_master_password,
|
||||
const std::string &new_master_password)
|
||||
{
|
||||
try {
|
||||
bool result = false;
|
||||
if (m_masterHash.length() == 0 && old_master_password.empty()) {
|
||||
// Nothing set yet so we initialize for first use
|
||||
m_keySalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
|
||||
m_masterKey = generateKey(new_master_password, m_keySalt.begin(), m_keySalt.length());
|
||||
|
||||
m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
|
||||
m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
} catch (...) {
|
||||
return Expected<bool>::fromException();
|
||||
}
|
||||
}
|
||||
|
||||
void PasswordManager::lock()
|
||||
{
|
||||
m_masterKey = StrengthenedKey();
|
||||
}
|
||||
|
||||
bool PasswordManager::locked() const
|
||||
{
|
||||
return m_masterKey.cipher_key.size() == 0;
|
||||
}
|
||||
|
||||
Expected<void> PasswordManager::savePassword(const std::string &key, const std::string &password)
|
||||
{
|
||||
if (locked()) {
|
||||
return Expected<void>::fromException(std::logic_error("Need to unlock the password manager first"));
|
||||
}
|
||||
std::string epw = encrypt(password, m_masterKey);
|
||||
m_store.emplace(key, epw);
|
||||
|
||||
return Expected<void>();
|
||||
}
|
||||
|
||||
Expected<bool> PasswordManager::getPassword(const std::string &key, std::string &out)
|
||||
{
|
||||
if (locked()) {
|
||||
return Expected<bool>::fromException(std::logic_error("Need to unlock the password manager first"));
|
||||
}
|
||||
auto fi = m_store.find(key);
|
||||
|
||||
bool result = false;
|
||||
if (fi != m_store.end()) {
|
||||
out = decrypt(fi->second, m_masterKey);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Botan::OctetString PasswordManager::hashStrengthenedKey(const StrengthenedKey &key, const OctetString &salt)
|
||||
{
|
||||
std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
|
||||
BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available");
|
||||
hash3->update(salt.begin(), salt.length());
|
||||
hash3->update(key.cipher_key.begin(), key.cipher_key.length());
|
||||
hash3->update(key.mac_key.begin(), key.mac_key.length());
|
||||
hash3->update(key.iv.begin(), key.iv.length());
|
||||
return hash3->final();
|
||||
}
|
||||
66
core/PasswordManager.h
Normal file
66
core/PasswordManager.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef PASSWORDMANAGER_H
|
||||
#define PASSWORDMANAGER_H
|
||||
|
||||
#include "Expected.h"
|
||||
#include <string>
|
||||
|
||||
#include <botan/botan.h>
|
||||
#include <botan/symkey.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
struct StrengthenedKey {
|
||||
Botan::SymmetricKey cipher_key;
|
||||
Botan::SymmetricKey mac_key;
|
||||
Botan::InitializationVector iv;
|
||||
|
||||
StrengthenedKey() {}
|
||||
StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk,
|
||||
const Botan::InitializationVector &i)
|
||||
: cipher_key(ck)
|
||||
, mac_key(mk)
|
||||
, iv(i)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
class PasswordManager {
|
||||
public:
|
||||
|
||||
// static PasswordManager create(const std::string &file_name);
|
||||
|
||||
PasswordManager();
|
||||
/** Unlocks the passwords of the connections.
|
||||
*
|
||||
* \return Normally it return a bool specifying if the password was accepted.
|
||||
* on rare occasions it could return an error.
|
||||
*/
|
||||
Expected<bool> unlock(const std::string &master_password);
|
||||
|
||||
Expected<bool> changeMasterPassword(const std::string &master_password,
|
||||
const std::string &new_master_password);
|
||||
|
||||
/** Forget master password
|
||||
*/
|
||||
void lock();
|
||||
bool locked() const;
|
||||
|
||||
Expected<void> savePassword(const std::string &key, const std::string &password);
|
||||
Expected<bool> getPassword(const std::string &key, std::string &out);
|
||||
|
||||
private:
|
||||
Botan::AutoSeeded_RNG m_rng;
|
||||
Botan::OctetString m_keySalt; // salt for generating crypto key
|
||||
StrengthenedKey m_masterKey; // crypto key
|
||||
Botan::OctetString m_hashSalt; // salt of the hash of the passphrase
|
||||
Botan::OctetString m_masterHash; // hash for checking the passphrase
|
||||
|
||||
using t_KeyPasswords = std::map<std::string, std::string>;
|
||||
|
||||
t_KeyPasswords m_store;
|
||||
|
||||
static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt);
|
||||
};
|
||||
|
||||
|
||||
#endif // PASSWORDMANAGER_H
|
||||
69
core/ScopeGuard.h
Normal file
69
core/ScopeGuard.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef SCOPEGUARD_H
|
||||
#define SCOPEGUARD_H
|
||||
|
||||
/** \brief Template class for executing code at scope exit.
|
||||
*
|
||||
* By default the object will be an active mode and execute the function
|
||||
* passed to the constructor when the object is destructed. You can however
|
||||
* cancel this action by calling dismiss().
|
||||
*
|
||||
* There is a clever macro that allows you to write something like
|
||||
* SCOPE_EXIT { foo(); };
|
||||
*/
|
||||
template<class Fun>
|
||||
class ScopeGuard {
|
||||
Fun f_;
|
||||
bool active_;
|
||||
public:
|
||||
ScopeGuard(Fun f)
|
||||
: f_(std::move(f))
|
||||
, active_(true) {
|
||||
}
|
||||
|
||||
~ScopeGuard() { if(active_) f_(); }
|
||||
|
||||
void dismiss() { active_=false; }
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs)
|
||||
: f_(std::move(rhs.f_))
|
||||
, active_(rhs.active_)
|
||||
{
|
||||
rhs.dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Fun>
|
||||
ScopeGuard<Fun> scopeGuard(Fun f)
|
||||
{
|
||||
return ScopeGuard<Fun>(std::move(f));
|
||||
}
|
||||
|
||||
namespace ScopeGuard_detail {
|
||||
|
||||
enum class ScopeGuardOnExit {};
|
||||
|
||||
template<typename Fun>
|
||||
ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) {
|
||||
return ScopeGuard<Fun>(std::forward<Fun>(fn));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str,__COUNTER__)
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str,__LINE__)
|
||||
#endif
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::ScopeGuard_detail::ScopeGuardOnExit() + [&]()
|
||||
|
||||
#endif // SCOPEGUARD_H
|
||||
162
core/SqlLexer.cpp
Normal file
162
core/SqlLexer.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include "SqlLexer.h"
|
||||
|
||||
SqlLexer::SqlLexer(const QString &block, LexerState currentstate)
|
||||
: m_block(block)
|
||||
, m_state(currentstate)
|
||||
{}
|
||||
|
||||
QChar SqlLexer::nextChar()
|
||||
{
|
||||
QChar result = QChar::Null;
|
||||
if (m_pos < m_block.size()) {
|
||||
result = m_block.at(m_pos++);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QChar SqlLexer::peekChar()
|
||||
{
|
||||
QChar result = QChar::Null;
|
||||
if (m_pos < m_block.size()) {
|
||||
result = m_block.at(m_pos);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief NextBasicToken
|
||||
* @param in
|
||||
* @param ofs
|
||||
* @param start
|
||||
* @param length
|
||||
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
|
||||
*/
|
||||
bool SqlLexer::nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out)
|
||||
{
|
||||
// Basically chops based on white space
|
||||
// it does also recognize comments and quoted strings/identifiers
|
||||
while (true) {
|
||||
startpos = m_pos;
|
||||
QChar c = nextChar();
|
||||
// if (LexerState::Null == m_state) {
|
||||
if (c.isSpace()) {
|
||||
// Just skip whitespace
|
||||
}
|
||||
else if (c == '-' && peekChar() == '-') { // two dashes, start of comment
|
||||
// Loop till end of line or end of block
|
||||
c = nextChar();
|
||||
for (;;) {
|
||||
c = peekChar();
|
||||
if (c != QChar::Null && c != '\n')
|
||||
nextChar();
|
||||
else
|
||||
break;
|
||||
}
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::Comment;
|
||||
return true;
|
||||
}
|
||||
else if (c == '\'') {
|
||||
// Single quoted string so it's an SQL text literal
|
||||
while (true) {
|
||||
c = peekChar();
|
||||
if (c == QChar::Null || c == '\n') {
|
||||
// unexpected end, pretend nothings wrong
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::QuotedString;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
nextChar();
|
||||
if (c == '\'') {
|
||||
// maybe end of string literal
|
||||
if (peekChar() == '\'') {
|
||||
// Nope, just double quote to escape quote
|
||||
nextChar(); // eat it
|
||||
}
|
||||
else {
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::QuotedString;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c == '"') {
|
||||
// Double quoted identifier
|
||||
while (true) {
|
||||
c = peekChar();
|
||||
if (c == QChar::Null || c == '\n') {
|
||||
// unexpected end, pretend nothings wrong
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::QuotedIdentifier;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
nextChar();
|
||||
if (c == '"') {
|
||||
// maybe end of string literal
|
||||
if (peekChar() == '"') {
|
||||
// Nope, just double quote to escape quote
|
||||
nextChar(); // eat it
|
||||
}
|
||||
else {
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::QuotedIdentifier;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// else if (c == '/' && peekChar() == '*') {
|
||||
// nextChar();
|
||||
// m_state = LexerState::InBlockComment;
|
||||
// }
|
||||
else if (c == QChar::Null) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Undetermined symbol
|
||||
for (;;) {
|
||||
c = peekChar();
|
||||
if (c.isLetterOrNumber() || c == '_')
|
||||
nextChar();
|
||||
else
|
||||
break;
|
||||
}
|
||||
length = m_pos - startpos;
|
||||
tokentype = BasicTokenType::Symbol;
|
||||
QStringRef sr(&m_block, startpos, length);
|
||||
out = sr.toString();
|
||||
return true;
|
||||
}
|
||||
// }
|
||||
// else if (LexerState::InBlockComment == m_state) {
|
||||
// if (c == QChar::Null) {
|
||||
// // eof current buffer, we need to return state so
|
||||
// if (m_pos == startpos) {
|
||||
// break;
|
||||
// }
|
||||
// else {
|
||||
// length = m_pos - startpos;
|
||||
// tokentype = BasicTokenType::OpenBlockComment;
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// else if (c == '*') {
|
||||
// nextChar();
|
||||
// if (peekChar() == '/') {
|
||||
// nextChar();
|
||||
// length = m_pos - startpos;
|
||||
// tokentype = BasicTokenType::BlockComment;
|
||||
// m_state = LexerState::Null;
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
49
core/SqlLexer.h
Normal file
49
core/SqlLexer.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef SQLLEXER_H
|
||||
#define SQLLEXER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
enum class BasicTokenType {
|
||||
None,
|
||||
End, // End of input
|
||||
Symbol, // can be many things, keyword, object name, operator, ..
|
||||
Comment,
|
||||
BlockComment,
|
||||
OpenBlockComment, // Busy with a block comment end not detected before end of current input
|
||||
QuotedString,
|
||||
DollarQuotedString,
|
||||
QuotedIdentifier
|
||||
};
|
||||
|
||||
enum class LexerState {
|
||||
Null,
|
||||
InDollarQuotedString,
|
||||
InBlockComment
|
||||
};
|
||||
|
||||
|
||||
class SqlLexer {
|
||||
public:
|
||||
SqlLexer(const QString &block, LexerState currentstate);
|
||||
QChar nextChar();
|
||||
QChar peekChar();
|
||||
/**
|
||||
* @brief NextBasicToken
|
||||
* @param in
|
||||
* @param ofs
|
||||
* @param start
|
||||
* @param length
|
||||
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
|
||||
*/
|
||||
bool nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out);
|
||||
|
||||
LexerState currentState() const { return m_state; }
|
||||
private:
|
||||
QString m_block;
|
||||
int m_pos = 0;
|
||||
LexerState m_state;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // SQLLEXER_H
|
||||
43
core/core.pro
Normal file
43
core/core.pro
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2017-02-26T10:51:14
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT -= gui
|
||||
|
||||
TARGET = core
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib c++11
|
||||
|
||||
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
|
||||
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
|
||||
LIBS += /LIBPATH:C:\VSproj\boost_1_63_0\stage\lib /LIBPATH:c:\prog\lib\ libpq.lib fmt.lib User32.lib ws2_32.lib
|
||||
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += Core.cpp \
|
||||
my_boost_assert_handler.cpp \
|
||||
SqlLexer.cpp \
|
||||
PasswordManager.cpp \
|
||||
CsvWriter.cpp
|
||||
|
||||
HEADERS += Core.h \
|
||||
PasswordManager.h \
|
||||
SqlLexer.h \
|
||||
ScopeGuard.h \
|
||||
CsvWriter.h
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
INSTALLS += target
|
||||
}
|
||||
20
core/my_boost_assert_handler.cpp
Normal file
20
core/my_boost_assert_handler.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
void assertion_failed(char const * expr, char const * function, char const * file, long line)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Assertion failure int " << function << " " << file << ":" << line;
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
|
||||
void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Assertion failure int " << function << " " << file << ":" << line << "\n"<< msg;
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue