Replaced old PasswordManager code with code using Botan's new PSK_Database
This greatly reduces the amount of encryption related code required. Thought we still have todo our own key strenthening but this also is easier with Botan::PasswordHash.
This commit is contained in:
parent
1ae9a1151a
commit
6b9b602c64
4 changed files with 294 additions and 305 deletions
|
|
@ -1,245 +1,180 @@
|
||||||
#include "PasswordManager.h"
|
#include "PasswordManager.h"
|
||||||
|
|
||||||
#include <botan/filters.h>
|
//#include <botan/filters.h>
|
||||||
#include <botan/pipe.h>
|
//#include <botan/pipe.h>
|
||||||
#include <botan/sha2_64.h>
|
//#include <botan/sha2_64.h>
|
||||||
#include <botan/hash.h>
|
//#include <botan/hash.h>
|
||||||
#include <botan/hmac.h>
|
//#include <botan/hmac.h>
|
||||||
#include <botan/pbkdf2.h>
|
//#include <botan/pbkdf2.h>
|
||||||
#include <botan/rng.h>
|
//#include <botan/rng.h>
|
||||||
|
//#include <botan/base64.h>
|
||||||
|
//#include <botan/loadstor.h>
|
||||||
|
//#include <botan/mem_ops.h>
|
||||||
|
#include <botan/auto_rng.h>
|
||||||
#include <botan/base64.h>
|
#include <botan/base64.h>
|
||||||
#include <botan/loadstor.h>
|
#include <botan/psk_db_sql.h>
|
||||||
#include <botan/mem_ops.h>
|
#include <botan/sqlite3.h>
|
||||||
|
#include <botan/scrypt.h>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
|
||||||
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,
|
Botan::secure_vector<uint8_t> PasswordManager::KeyStrengthener::derive(const std::string &passphrase)
|
||||||
int saltlength, int iterations)
|
|
||||||
{
|
|
||||||
PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
|
|
||||||
OctetString master_key = pbkdf.derive_key(
|
|
||||||
PBKDF_OUTPUT_LEN,
|
|
||||||
passphrase,
|
|
||||||
salt, saltlength,
|
|
||||||
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(int iterations)
|
|
||||||
: m_iterations(iterations)
|
|
||||||
{
|
{
|
||||||
|
Botan::secure_vector<uint8_t> master_key(m_keySize);
|
||||||
|
m_hasher->derive_key(master_key.data(), master_key.size(), passphrase.c_str(), passphrase.length(), m_salt.data(), m_salt.size());
|
||||||
|
|
||||||
|
return master_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<bool> PasswordManager::unlock(const std::string &master_password)
|
void PasswordManager::KeyStrengthener::saveParams(std::shared_ptr<Botan::Sqlite3_Database> db, const std::string &table_name)
|
||||||
{
|
{
|
||||||
try {
|
auto sc = dynamic_cast<Botan::Scrypt*>(m_hasher.get());
|
||||||
bool result = false;
|
size_t i1 = sc->N();
|
||||||
if (m_masterHash.length() == 0 && master_password.empty()) {
|
size_t i2 = sc->r();
|
||||||
result = true;
|
size_t i3 = sc->p();
|
||||||
} else {
|
|
||||||
StrengthenedKey key = generateKey(master_password, m_keySalt.begin(),
|
|
||||||
m_keySalt.length(), m_iterations);
|
|
||||||
OctetString hash = hashStrengthenedKey(key, m_hashSalt);
|
|
||||||
|
|
||||||
BOOST_ASSERT_MSG(hash.length() == m_masterHash.length(), "Both hashes should have the same length! Versioning error?");
|
// SAVE parameters in database
|
||||||
|
auto stmt = db->new_statement("INSERT INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) VALUES(?1, ?2, ?3, ?4, ?5)");
|
||||||
|
stmt->bind(1, 1);
|
||||||
|
stmt->bind(2, "Scrypt");
|
||||||
|
stmt->bind(3, i1);
|
||||||
|
stmt->bind(4, i2);
|
||||||
|
stmt->bind(5, i3);
|
||||||
|
stmt->bind(6, m_keySize);
|
||||||
|
stmt->bind(7, Botan::base64_encode(m_salt));
|
||||||
|
stmt->spin();
|
||||||
|
|
||||||
if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) {
|
}
|
||||||
result = true;
|
|
||||||
m_masterKey = key;
|
// -------------------------
|
||||||
|
|
||||||
|
void PasswordManager::openDatabase(std::shared_ptr<Botan::Sqlite3_Database> db, std::string passphrase)
|
||||||
|
{
|
||||||
|
// std::string psk_db_file_name;
|
||||||
|
// auto db = std::make_shared<Botan::Sqlite3_Database>(psk_db_file_name);
|
||||||
|
|
||||||
|
KeyStrengthener ks;
|
||||||
|
// if (database exists)
|
||||||
|
if (isPskStoreInitialized(db)) {
|
||||||
|
ks = getKeyStrengthener(db);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
initializeNewPskStore(db);
|
||||||
|
ks = createKeyStrengthener();
|
||||||
|
ks.saveParams(db, m_secretAlgoTableName);
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
} catch (...) {
|
Botan::secure_vector<uint8_t> master_key = ks.derive(passphrase);
|
||||||
return Expected<bool>::fromException();
|
m_pskDatabase = std::make_unique<Botan::Encrypted_PSK_Database_SQL>(master_key, db, m_passwordTableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PasswordManager::closeDatabase()
|
||||||
|
{
|
||||||
|
m_pskDatabase.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PasswordManager::set(const std::string &id, const std::string &passwd)
|
||||||
|
{
|
||||||
|
if (m_pskDatabase) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw PasswordManagerLockedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<bool> PasswordManager::changeMasterPassword(const std::string &old_master_password,
|
std::string PasswordManager::get(const std::string &id, const std::string &passwd)
|
||||||
const std::string &new_master_password)
|
|
||||||
{
|
{
|
||||||
try {
|
if (m_pskDatabase) {
|
||||||
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_iterations);
|
|
||||||
|
|
||||||
m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
|
|
||||||
m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt);
|
|
||||||
result = true;
|
|
||||||
}
|
}
|
||||||
return result;
|
else {
|
||||||
} catch (...) {
|
throw PasswordManagerLockedException();
|
||||||
return Expected<bool>::fromException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasswordManager::lock()
|
void PasswordManager::remove(const std::string &id)
|
||||||
{
|
{
|
||||||
m_masterKey = StrengthenedKey();
|
if (m_pskDatabase) {
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
else {
|
||||||
m_store.emplace(key, epw);
|
throw PasswordManagerLockedException();
|
||||||
|
|
||||||
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)
|
|
||||||
|
void PasswordManager::initializeNewPskStore(std::shared_ptr<Botan::Sqlite3_Database> db)
|
||||||
{
|
{
|
||||||
std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
|
// Create tables
|
||||||
BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available");
|
// - psk_masterkey_algo
|
||||||
hash3->update(salt.begin(), salt.length());
|
// - psk_passwd
|
||||||
hash3->update(key.cipher_key.begin(), key.cipher_key.length());
|
std::string create_statement =
|
||||||
hash3->update(key.mac_key.begin(), key.mac_key.length());
|
"CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n"
|
||||||
hash3->update(key.iv.begin(), key.iv.length());
|
" id INTEGER PRIMARY KEY \n"
|
||||||
return hash3->final();
|
" algo TEXT \n"
|
||||||
|
" i1 INTEGER \n"
|
||||||
|
" i2 INTEGER \n"
|
||||||
|
" i3 INTEGER \n"
|
||||||
|
" ks INTEGER \n"
|
||||||
|
" salt TEXT \n"
|
||||||
|
");";
|
||||||
|
db->create_table(create_statement);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordManager::isPskStoreInitialized(std::shared_ptr<Botan::Sqlite3_Database> db)
|
||||||
|
{
|
||||||
|
// Is the table with the secret data present and filled?
|
||||||
|
auto stmt = db->new_statement("SELECT name FROM sqlite_master WHERE type='table' AND name=?1");
|
||||||
|
stmt->bind(1, m_secretAlgoTableName);
|
||||||
|
bool ok = stmt->step();
|
||||||
|
if (ok) {
|
||||||
|
auto stmt = db->new_statement("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1");
|
||||||
|
return stmt->step();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordManager::KeyStrengthener PasswordManager::getKeyStrengthener(std::shared_ptr<Botan::Sqlite3_Database> db)
|
||||||
|
{
|
||||||
|
auto stmt = db->new_statement("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1");
|
||||||
|
if (stmt->step()) {
|
||||||
|
std::string algo = stmt->get_str(0);
|
||||||
|
size_t i1 = boost::lexical_cast<size_t>(stmt->get_str(1));
|
||||||
|
size_t i2 = boost::lexical_cast<size_t>(stmt->get_str(2));
|
||||||
|
size_t i3 = boost::lexical_cast<size_t>(stmt->get_str(3));
|
||||||
|
size_t ks = boost::lexical_cast<size_t>(stmt->get_str(4));
|
||||||
|
|
||||||
|
auto pwh_fam = Botan::PasswordHashFamily::create(algo);
|
||||||
|
return KeyStrengthener(
|
||||||
|
pwh_fam->from_params(i1, i2, i3),
|
||||||
|
Botan::base64_decode(stmt->get_str(5)),
|
||||||
|
ks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordManager::KeyStrengthener PasswordManager::createKeyStrengthener()
|
||||||
|
{
|
||||||
|
// std::unique_ptr<Botan::PasswordHash> pwh;
|
||||||
|
|
||||||
|
size_t key_size = 64;
|
||||||
|
Botan::secure_vector<uint8_t> salt(key_size);
|
||||||
|
Botan::AutoSeeded_RNG rng;
|
||||||
|
rng.randomize(salt.data(), salt.size());
|
||||||
|
|
||||||
|
const std::string algo = "Scrypt";
|
||||||
|
auto pwh_fam = Botan::PasswordHashFamily::create(algo);
|
||||||
|
return KeyStrengthener(
|
||||||
|
pwh_fam->tune(key_size, std::chrono::seconds(2), 130),
|
||||||
|
salt,
|
||||||
|
key_size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,65 +2,100 @@
|
||||||
#define PASSWORDMANAGER_H
|
#define PASSWORDMANAGER_H
|
||||||
|
|
||||||
#include "Expected.h"
|
#include "Expected.h"
|
||||||
|
#include <botan/secmem.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <botan/botan.h>
|
#include <botan/pwdhash.h>
|
||||||
#include <botan/symkey.h>
|
|
||||||
|
|
||||||
|
//#include <botan/botan.h>
|
||||||
|
//#include <botan/symkey.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
struct StrengthenedKey {
|
namespace Botan {
|
||||||
Botan::SymmetricKey cipher_key;
|
|
||||||
Botan::SymmetricKey mac_key;
|
|
||||||
Botan::InitializationVector iv;
|
|
||||||
|
|
||||||
StrengthenedKey() {}
|
class Encrypted_PSK_Database_SQL;
|
||||||
StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk,
|
class Sqlite3_Database;
|
||||||
const Botan::InitializationVector &i)
|
class PasswordHash;
|
||||||
: cipher_key(ck)
|
|
||||||
, mac_key(mk)
|
}
|
||||||
, iv(i)
|
|
||||||
{}
|
class PasswordManagerException: public std::exception {
|
||||||
|
public:
|
||||||
|
using std::exception::exception; //(char const* const _Message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PasswordManagerLockedException: public PasswordManagerException {
|
||||||
|
public:
|
||||||
|
using PasswordManagerException::PasswordManagerException;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class PasswordManager {
|
class PasswordManager {
|
||||||
public:
|
public:
|
||||||
|
enum Result {
|
||||||
|
Ok,
|
||||||
|
Locked,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
// static PasswordManager create(const std::string &file_name);
|
PasswordManager() = default;
|
||||||
|
|
||||||
explicit PasswordManager(int iterations = 8192);
|
void openDatabase(std::shared_ptr<Botan::Sqlite3_Database> db, std::string passphrase);
|
||||||
/** Unlocks the passwords of the connections.
|
void closeDatabase();
|
||||||
*
|
|
||||||
* \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);
|
|
||||||
|
|
||||||
|
void set(const std::string &id, const std::string &passwd);
|
||||||
|
std::string get(const std::string &id, const std::string &passwd);
|
||||||
|
void remove(const std::string &id);
|
||||||
private:
|
private:
|
||||||
int m_iterations;
|
std::string m_passwordTableName = "psk_passwd";
|
||||||
Botan::AutoSeeded_RNG m_rng;
|
std::string m_secretAlgoTableName = "psk_masterkey_algo";
|
||||||
Botan::OctetString m_keySalt; // salt for generating crypto key
|
std::unique_ptr<Botan::Encrypted_PSK_Database_SQL> m_pskDatabase;
|
||||||
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>;
|
bool isPskStoreInitialized(std::shared_ptr<Botan::Sqlite3_Database> db);
|
||||||
|
void initializeNewPskStore(std::shared_ptr<Botan::Sqlite3_Database> db);
|
||||||
|
|
||||||
t_KeyPasswords m_store;
|
class KeyStrengthener {
|
||||||
|
public:
|
||||||
|
KeyStrengthener() = default;
|
||||||
|
KeyStrengthener(std::unique_ptr<Botan::PasswordHash> hasher, Botan::secure_vector<uint8_t> salt, size_t keysize)
|
||||||
|
: m_hasher (std::move(hasher))
|
||||||
|
, m_salt (std::move(salt))
|
||||||
|
, m_keySize(keysize)
|
||||||
|
{}
|
||||||
|
|
||||||
|
KeyStrengthener(const KeyStrengthener&) = delete;
|
||||||
|
KeyStrengthener& operator=(const KeyStrengthener &) = delete;
|
||||||
|
|
||||||
|
KeyStrengthener(KeyStrengthener &&rhs)
|
||||||
|
: m_hasher (std::move(rhs.m_hasher))
|
||||||
|
, m_salt (std::move(rhs.m_salt))
|
||||||
|
, m_keySize(rhs.m_keySize)
|
||||||
|
{}
|
||||||
|
KeyStrengthener& operator=(KeyStrengthener &&rhs)
|
||||||
|
{
|
||||||
|
if (&rhs != this) {
|
||||||
|
m_hasher = std::move(rhs.m_hasher);
|
||||||
|
m_salt = std::move(rhs.m_salt);
|
||||||
|
m_keySize = rhs.m_keySize;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Botan::secure_vector<uint8_t> derive(const std::string &passphrase);
|
||||||
|
void saveParams(std::shared_ptr<Botan::Sqlite3_Database> db, const std::string &table_name);
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Botan::PasswordHash> m_hasher;
|
||||||
|
Botan::secure_vector<uint8_t> m_salt;
|
||||||
|
size_t m_keySize;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get PasswordHash from parameters in database
|
||||||
|
KeyStrengthener getKeyStrengthener(std::shared_ptr<Botan::Sqlite3_Database> db);
|
||||||
|
KeyStrengthener createKeyStrengthener();
|
||||||
|
|
||||||
static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ C:\VSproj\boost32\include
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD/../../core
|
INCLUDEPATH += $$PWD/../../core
|
||||||
DEPENDPATH += $$PWD/../../core
|
DEPENDPATH += $$PWD/../../core
|
||||||
LIBS += c:\prog\lib\botand_imp.lib
|
LIBS += c:\prog\lib\botan_imp.lib
|
||||||
|
|
||||||
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a
|
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a
|
||||||
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a
|
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a
|
||||||
|
|
|
||||||
|
|
@ -6,69 +6,88 @@
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
|
|
||||||
TEST(PasswordManager, initial_changeMasterPassword_returns_true)
|
#include <botan/pwdhash.h>
|
||||||
{
|
#include <botan/scrypt.h>
|
||||||
PasswordManager pwm(10);
|
|
||||||
|
|
||||||
auto res = pwm.changeMasterPassword("", "my test passphrase");
|
//TEST(Botan, recreate)
|
||||||
ASSERT_NO_THROW( res.get() );
|
//{
|
||||||
ASSERT_THAT( res.get(), Eq(true) );
|
// auto phf = Botan::PasswordHashFamily::create("Scrypt");
|
||||||
}
|
// size_t N = 65536, r = 10, p = 2;
|
||||||
|
// auto ph = phf->from_params(N, r, p);
|
||||||
|
// auto sc = dynamic_cast<Botan::Scrypt*>(ph.get());
|
||||||
|
|
||||||
TEST(PasswordManager, unlock_succeeds)
|
// ASSERT_EQ(N, sc->N());
|
||||||
{
|
// ASSERT_EQ(r, sc->r());
|
||||||
PasswordManager pwm(10);
|
// ASSERT_EQ(p, sc->p());
|
||||||
|
|
||||||
std::string passphrase = "my test passphrase";
|
// auto phf2 = phf->create(ph->to_string());
|
||||||
|
// phf2->default_params()
|
||||||
|
|
||||||
auto res = pwm.changeMasterPassword("", passphrase);
|
//}
|
||||||
ASSERT_NO_THROW( res.get() );
|
|
||||||
ASSERT_THAT( res.get(), Eq(true) );
|
|
||||||
|
|
||||||
auto res2 = pwm.unlock(passphrase);
|
//TEST(PasswordManager, initial_changeMasterPassword_returns_true)
|
||||||
ASSERT_NO_THROW( res2.get() );
|
//{
|
||||||
ASSERT_THAT( res2.get(), Eq(true) );
|
// PasswordManager pwm(10);
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PasswordManager, unlock_fails)
|
// auto res = pwm.changeMasterPassword("", "my test passphrase");
|
||||||
{
|
// ASSERT_NO_THROW( res.get() );
|
||||||
PasswordManager pwm(10);
|
// ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
//}
|
||||||
|
|
||||||
std::string passphrase = "my test passphrase";
|
//TEST(PasswordManager, unlock_succeeds)
|
||||||
|
//{
|
||||||
|
// PasswordManager pwm(10);
|
||||||
|
|
||||||
auto res = pwm.changeMasterPassword("", passphrase);
|
// std::string passphrase = "my test passphrase";
|
||||||
ASSERT_NO_THROW( res.get() );
|
|
||||||
ASSERT_THAT( res.get(), Eq(true) );
|
|
||||||
|
|
||||||
auto res2 = pwm.unlock(passphrase + "2");
|
// auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
ASSERT_NO_THROW( res2.get() );
|
// ASSERT_NO_THROW( res.get() );
|
||||||
ASSERT_THAT( res2.get(), Eq(false) );
|
// ASSERT_THAT( res.get(), Eq(true) );
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PasswordManager, test_save_get)
|
// auto res2 = pwm.unlock(passphrase);
|
||||||
{
|
// ASSERT_NO_THROW( res2.get() );
|
||||||
PasswordManager pwm(10);
|
// ASSERT_THAT( res2.get(), Eq(true) );
|
||||||
|
//}
|
||||||
|
|
||||||
std::string passphrase = "my test passphrase";
|
//TEST(PasswordManager, unlock_fails)
|
||||||
|
//{
|
||||||
|
// PasswordManager pwm(10);
|
||||||
|
|
||||||
auto res = pwm.changeMasterPassword("", passphrase);
|
// std::string passphrase = "my test passphrase";
|
||||||
ASSERT_NO_THROW( res.get() );
|
|
||||||
ASSERT_THAT( res.get(), Eq(true) );
|
// auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
|
// ASSERT_NO_THROW( res.get() );
|
||||||
|
// ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
|
||||||
// auto res2 = pwm.unlock(passphrase + "2");
|
// auto res2 = pwm.unlock(passphrase + "2");
|
||||||
// ASSERT_NO_THROW( res2.get() );
|
// ASSERT_NO_THROW( res2.get() );
|
||||||
// ASSERT_THAT( res2.get(), Eq(false) );
|
// ASSERT_THAT( res2.get(), Eq(false) );
|
||||||
|
//}
|
||||||
|
|
||||||
const std::string password = "password123";
|
//TEST(PasswordManager, test_save_get)
|
||||||
const std::string key = "abc";
|
//{
|
||||||
|
// PasswordManager pwm(10);
|
||||||
|
|
||||||
auto res2 = pwm.savePassword(key, password);
|
// std::string passphrase = "my test passphrase";
|
||||||
ASSERT_THAT( res2.valid(), Eq(true) );
|
|
||||||
|
|
||||||
std::string result;
|
// auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
auto res3 = pwm.getPassword(key, result);
|
// ASSERT_NO_THROW( res.get() );
|
||||||
ASSERT_THAT( res3.valid(), Eq(true) );
|
// ASSERT_THAT( res.get(), Eq(true) );
|
||||||
ASSERT_THAT( res3.get(), Eq(true) );
|
|
||||||
ASSERT_THAT( result, Eq(password) );
|
|
||||||
|
|
||||||
}
|
//// auto res2 = pwm.unlock(passphrase + "2");
|
||||||
|
//// ASSERT_NO_THROW( res2.get() );
|
||||||
|
//// ASSERT_THAT( res2.get(), Eq(false) );
|
||||||
|
|
||||||
|
// const std::string password = "password123";
|
||||||
|
// const std::string key = "abc";
|
||||||
|
|
||||||
|
// auto res2 = pwm.savePassword(key, password);
|
||||||
|
// ASSERT_THAT( res2.valid(), Eq(true) );
|
||||||
|
|
||||||
|
// std::string result;
|
||||||
|
// auto res3 = pwm.getPassword(key, result);
|
||||||
|
// ASSERT_THAT( res3.valid(), Eq(true) );
|
||||||
|
// ASSERT_THAT( res3.get(), Eq(true) );
|
||||||
|
// ASSERT_THAT( result, Eq(password) );
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue