diff --git a/core/PasswordManager.cpp b/core/PasswordManager.cpp index cf60e9d..166cde5 100644 --- a/core/PasswordManager.cpp +++ b/core/PasswordManager.cpp @@ -1,245 +1,180 @@ #include "PasswordManager.h" -#include -#include -#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include #include -#include -#include - -#include - - -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"; +#include +#include +#include +#include - StrengthenedKey generateKey(const std::string &passphrase, const uint8_t *salt, - 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 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 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 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 PasswordManager::KeyStrengthener::derive(const std::string &passphrase) { + Botan::secure_vector 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 PasswordManager::unlock(const std::string &master_password) +void PasswordManager::KeyStrengthener::saveParams(std::shared_ptr db, const std::string &table_name) { - 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(), m_iterations); - OctetString hash = hashStrengthenedKey(key, m_hashSalt); + auto sc = dynamic_cast(m_hasher.get()); + size_t i1 = sc->N(); + size_t i2 = sc->r(); + size_t i3 = sc->p(); - 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; - } - } - return result; - } catch (...) { - return Expected::fromException(); +} + +// ------------------------- + +void PasswordManager::openDatabase(std::shared_ptr db, std::string passphrase) +{ +// std::string psk_db_file_name; +// auto db = std::make_shared(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); + } + + Botan::secure_vector master_key = ks.derive(passphrase); + m_pskDatabase = std::make_unique(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 PasswordManager::changeMasterPassword(const std::string &old_master_password, - const std::string &new_master_password) +std::string PasswordManager::get(const std::string &id, const std::string &passwd) { - 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_iterations); + if (m_pskDatabase) { - m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len); - m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt); - result = true; - } - return result; - } catch (...) { - return Expected::fromException(); + } + else { + throw PasswordManagerLockedException(); } } -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 PasswordManager::savePassword(const std::string &key, const std::string &password) -{ - if (locked()) { - return Expected::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(); -} - -Expected PasswordManager::getPassword(const std::string &key, std::string &out) -{ - if (locked()) { - return Expected::fromException(std::logic_error("Need to unlock the password manager first")); + else { + throw PasswordManagerLockedException(); } - 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 db) { - std::unique_ptr 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(); + // Create tables + // - psk_masterkey_algo + // - psk_passwd + std::string create_statement = + "CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n" + " id INTEGER PRIMARY KEY \n" + " 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 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 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(stmt->get_str(1)); + size_t i2 = boost::lexical_cast(stmt->get_str(2)); + size_t i3 = boost::lexical_cast(stmt->get_str(3)); + size_t ks = boost::lexical_cast(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 pwh; + + size_t key_size = 64; + Botan::secure_vector 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 + ); } diff --git a/core/PasswordManager.h b/core/PasswordManager.h index 5ddd021..e014ebc 100644 --- a/core/PasswordManager.h +++ b/core/PasswordManager.h @@ -2,65 +2,100 @@ #define PASSWORDMANAGER_H #include "Expected.h" +#include #include +#include -#include -#include +#include + + +//#include +//#include #include -struct StrengthenedKey { - Botan::SymmetricKey cipher_key; - Botan::SymmetricKey mac_key; - Botan::InitializationVector iv; +namespace Botan { - StrengthenedKey() {} - StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk, - const Botan::InitializationVector &i) - : cipher_key(ck) - , mac_key(mk) - , iv(i) - {} + class Encrypted_PSK_Database_SQL; + class Sqlite3_Database; + class PasswordHash; + +} + +class PasswordManagerException: public std::exception { +public: + using std::exception::exception; //(char const* const _Message); }; +class PasswordManagerLockedException: public PasswordManagerException { +public: + using PasswordManagerException::PasswordManagerException; + +}; class PasswordManager { public: + enum Result { + Ok, + Locked, + Error + }; -// static PasswordManager create(const std::string &file_name); + PasswordManager() = default; - explicit PasswordManager(int iterations = 8192); - /** 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 unlock(const std::string &master_password); - - Expected changeMasterPassword(const std::string &master_password, - const std::string &new_master_password); - - /** Forget master password - */ - void lock(); - bool locked() const; - - Expected savePassword(const std::string &key, const std::string &password); - Expected getPassword(const std::string &key, std::string &out); + void openDatabase(std::shared_ptr db, std::string passphrase); + void closeDatabase(); + 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: - int m_iterations; - 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 + std::string m_passwordTableName = "psk_passwd"; + std::string m_secretAlgoTableName = "psk_masterkey_algo"; + std::unique_ptr m_pskDatabase; - using t_KeyPasswords = std::map; + bool isPskStoreInitialized(std::shared_ptr db); + void initializeNewPskStore(std::shared_ptr db); - t_KeyPasswords m_store; + class KeyStrengthener { + public: + KeyStrengthener() = default; + KeyStrengthener(std::unique_ptr hasher, Botan::secure_vector 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 derive(const std::string &passphrase); + void saveParams(std::shared_ptr db, const std::string &table_name); + private: + std::unique_ptr m_hasher; + Botan::secure_vector m_salt; + size_t m_keySize; + }; + + /// Get PasswordHash from parameters in database + KeyStrengthener getKeyStrengthener(std::shared_ptr db); + KeyStrengthener createKeyStrengthener(); - static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt); }; diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index eff3fce..3a9ad20 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -39,7 +39,7 @@ C:\VSproj\boost32\include INCLUDEPATH += $$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 else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a diff --git a/tests/pglabtests/tst_PasswordManager.cpp b/tests/pglabtests/tst_PasswordManager.cpp index 566dbf0..ab3fa74 100644 --- a/tests/pglabtests/tst_PasswordManager.cpp +++ b/tests/pglabtests/tst_PasswordManager.cpp @@ -6,69 +6,88 @@ using namespace testing; -TEST(PasswordManager, initial_changeMasterPassword_returns_true) -{ - PasswordManager pwm(10); +#include +#include - auto res = pwm.changeMasterPassword("", "my test passphrase"); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); -} +//TEST(Botan, recreate) +//{ +// 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(ph.get()); -TEST(PasswordManager, unlock_succeeds) -{ - PasswordManager pwm(10); +// ASSERT_EQ(N, sc->N()); +// ASSERT_EQ(r, sc->r()); +// 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); - ASSERT_NO_THROW( res2.get() ); - ASSERT_THAT( res2.get(), Eq(true) ); -} +//TEST(PasswordManager, initial_changeMasterPassword_returns_true) +//{ +// PasswordManager pwm(10); -TEST(PasswordManager, unlock_fails) -{ - PasswordManager pwm(10); +// auto res = pwm.changeMasterPassword("", "my test passphrase"); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); +//} - std::string passphrase = "my test passphrase"; +//TEST(PasswordManager, unlock_succeeds) +//{ +// PasswordManager pwm(10); - auto res = pwm.changeMasterPassword("", passphrase); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); +// std::string passphrase = "my test passphrase"; - auto res2 = pwm.unlock(passphrase + "2"); - ASSERT_NO_THROW( res2.get() ); - ASSERT_THAT( res2.get(), Eq(false) ); -} +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); -TEST(PasswordManager, test_save_get) -{ - PasswordManager pwm(10); +// auto res2 = pwm.unlock(passphrase); +// ASSERT_NO_THROW( res2.get() ); +// ASSERT_THAT( res2.get(), Eq(true) ); +//} - std::string passphrase = "my test passphrase"; +//TEST(PasswordManager, unlock_fails) +//{ +// PasswordManager pwm(10); - auto res = pwm.changeMasterPassword("", passphrase); - ASSERT_NO_THROW( res.get() ); - ASSERT_THAT( res.get(), Eq(true) ); +// std::string passphrase = "my test passphrase"; + +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); // 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"; +//TEST(PasswordManager, test_save_get) +//{ +// PasswordManager pwm(10); - auto res2 = pwm.savePassword(key, password); - ASSERT_THAT( res2.valid(), Eq(true) ); +// std::string passphrase = "my test passphrase"; - 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) ); +// auto res = pwm.changeMasterPassword("", passphrase); +// ASSERT_NO_THROW( res.get() ); +// ASSERT_THAT( res.get(), Eq(true) ); -} +//// 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) ); + +//}