mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
auto recover from corrupted mnemonic words by calling User::generateNewPassphrase
This commit is contained in:
parent
561826ddce
commit
2910d85729
@ -39,7 +39,7 @@ KeyPair::~KeyPair()
|
||||
|
||||
bool KeyPair::generateFromPassphrase(const char* passphrase, Mnemonic* word_source)
|
||||
{
|
||||
|
||||
auto er = ErrorManager::getInstance();
|
||||
// libsodium doc: https://libsodium.gitbook.io/doc/advanced/hmac-sha2
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
//crypto_auth_hmacsha512_keygen
|
||||
@ -53,7 +53,14 @@ bool KeyPair::generateFromPassphrase(const char* passphrase, Mnemonic* word_sour
|
||||
unsigned char word_cursor = 0;
|
||||
for (size_t i = 0; i < pass_phrase_size; i++) {
|
||||
if (passphrase[i] == ' ') {
|
||||
word_indices[word_cursor] = word_source->getWordIndex(acBuffer);
|
||||
if (word_source->isWordExist(acBuffer)) {
|
||||
word_indices[word_cursor] = word_source->getWordIndex(acBuffer);
|
||||
}
|
||||
else {
|
||||
er->addError(new ParamError("KeyPair::generateFromPassphrase", "word didn't exist", acBuffer));
|
||||
er->sendErrorsAsEmail();
|
||||
return false;
|
||||
}
|
||||
|
||||
word_cursor++;
|
||||
memset(acBuffer, 0, STR_BUFFER_SIZE);
|
||||
@ -90,6 +97,9 @@ bool KeyPair::generateFromPassphrase(const char* passphrase, Mnemonic* word_sour
|
||||
if (!mPrivateKey) {
|
||||
//delete mPrivateKey;
|
||||
mPrivateKey = mm->getFreeMemory(ed25519_privkey_SIZE);
|
||||
if (!mPrivateKey) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//mPrivateKey = new ObfusArray(ed25519_privkey_SIZE, prv_key_t.data);
|
||||
|
||||
|
||||
@ -15,19 +15,16 @@ Mnemonic::Mnemonic()
|
||||
|
||||
Mnemonic::~Mnemonic()
|
||||
{
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
if (mWords[i]) {
|
||||
free(mWords[i]);
|
||||
}
|
||||
}
|
||||
memset(mWords, 0, 2048);
|
||||
mWordHashIndices.clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Mnemonic::init(void(*fill_words_func)(unsigned char*), unsigned int original_size, unsigned int compressed_size)
|
||||
{
|
||||
Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500);
|
||||
clear();
|
||||
|
||||
unsigned char* buffer = (unsigned char*)malloc(compressed_size);
|
||||
unsigned char* uncompressed_buffer = (unsigned char*)malloc(original_size + 1);
|
||||
memset(uncompressed_buffer, 0, original_size + 1);
|
||||
@ -102,3 +99,29 @@ int Mnemonic::init(void(*fill_words_func)(unsigned char*), unsigned int original
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
void Mnemonic::clear()
|
||||
{
|
||||
//Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500);
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
if (mWords[i]) {
|
||||
free(mWords[i]);
|
||||
}
|
||||
}
|
||||
memset(mWords, 0, 2048);
|
||||
mWordHashIndices.clear();
|
||||
}
|
||||
|
||||
std::string Mnemonic::getCompleteWordList()
|
||||
{
|
||||
std::string result("");
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
if (mWords[i]) {
|
||||
result += std::to_string(i) + ": " + mWords[i] + "\n";
|
||||
}
|
||||
else {
|
||||
result += std::to_string(i) + ": <word empty>\n";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include "../lib/DRHashList.h"
|
||||
#include "Poco/Mutex.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
@ -26,15 +27,22 @@ public:
|
||||
|
||||
int init(void(*fill_words_func)(unsigned char*), unsigned int original_size, unsigned int compressed_size);
|
||||
|
||||
inline const char* getWord(unsigned int index) { if (index < 2048) return mWords[index]; return nullptr; }
|
||||
inline unsigned short getWordIndex(const char* word) { DHASH word_hash = DRMakeStringHash(word); return mWordHashIndices.find(word_hash)->second; }
|
||||
inline bool isWordExist(const std::string& word) { DHASH word_hash = DRMakeStringHash(word.data()); return mWordHashIndices.find(word_hash) != mWordHashIndices.end(); }
|
||||
inline const char* getWord(unsigned int index) { Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); if (index < 2048 && index >= 0) return mWords[index]; return nullptr; }
|
||||
inline unsigned short getWordIndex(const char* word) { Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); DHASH word_hash = DRMakeStringHash(word); return mWordHashIndices.find(word_hash)->second; }
|
||||
inline bool isWordExist(const std::string& word) { Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); DHASH word_hash = DRMakeStringHash(word.data()); return mWordHashIndices.find(word_hash) != mWordHashIndices.end(); }
|
||||
// using only for debugging
|
||||
std::string getCompleteWordList();
|
||||
|
||||
protected:
|
||||
|
||||
void clear();
|
||||
|
||||
char* mWords[2048];
|
||||
//DRHashList mWordHashIndices;
|
||||
typedef std::pair<DHASH, unsigned short> WordHashEntry;
|
||||
std::map<DHASH, unsigned short> mWordHashIndices;
|
||||
Poco::Mutex mWorkingMutex;
|
||||
|
||||
};
|
||||
|
||||
#endif //DR_MNEMONIC_H
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -148,6 +148,8 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
|
||||
errorLog.error("[Gradido_LoginServer::main] error loading mnemonic Word List");
|
||||
return Application::EXIT_CONFIG;
|
||||
}
|
||||
//printf("show mnemonic list: \n");
|
||||
//printf(ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER].getCompleteWordList().data());
|
||||
if (!ServerConfig::initServerCrypto(config())) {
|
||||
//printf("[Gradido_LoginServer::%s] error init server crypto\n", __FUNCTION__);
|
||||
errorLog.error("[Gradido_LoginServer::main] error init server crypto");
|
||||
|
||||
@ -53,6 +53,7 @@ namespace ServerConfig {
|
||||
bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg);
|
||||
bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg);
|
||||
bool initSSLClientContext();
|
||||
|
||||
|
||||
void writeToFile(std::istream& datas, std::string fileName);
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ int main(int argc, char** argv)
|
||||
testUser->validatePwd("haz27Newpassword", nullptr);
|
||||
ServerConfig::g_FakeLoginSleepTime = (int)std::round(timeUsed.millis());
|
||||
delete testUser;
|
||||
|
||||
|
||||
|
||||
Gradido_LoginServer app;
|
||||
return app.run(argc, argv);
|
||||
|
||||
@ -740,6 +740,7 @@ bool Session::useOrGeneratePassphrase(const std::string& passphase)
|
||||
bool Session::generatePassphrase()
|
||||
{
|
||||
mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]);
|
||||
|
||||
updateState(SESSION_STATE_PASSPHRASE_GENERATED);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3,11 +3,13 @@
|
||||
#include <sodium.h>
|
||||
#include "ed25519/ed25519.h"
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/RegularExpression.h"
|
||||
#include "../ServerConfig.h"
|
||||
|
||||
#include "../SingletonManager/ConnectionManager.h"
|
||||
#include "../SingletonManager/ErrorManager.h"
|
||||
#include "../SingletonManager/SessionManager.h"
|
||||
#include "../SingletonManager/LanguageManager.h"
|
||||
|
||||
|
||||
#include "Poco/Data/Binding.h"
|
||||
@ -46,7 +48,11 @@ int UserGenerateKeys::run()
|
||||
{
|
||||
|
||||
// always return true, cannot fail (only if low on memory)
|
||||
mKeys.generateFromPassphrase(mPassphrase.data(), &ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]);
|
||||
// !!! update: can no fail, if passphrase is invalid, for example if memory is corrupted
|
||||
if (!mKeys.generateFromPassphrase(mPassphrase.data(), &ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER])) {
|
||||
mUser->addError(new Error(mUser->gettext("User generate Keys"), mUser->gettext("invalid passphrase, please notice the server admin coin@gradido.net")));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mUser->setPublicKeyHex(mKeys.getPubkeyHex());
|
||||
mUser->setPublicKey(mKeys.getPublicKey());
|
||||
@ -183,6 +189,7 @@ User::User(const char* email, const char* first_name, const char* last_name)
|
||||
mLanguage(LANG_DE), mGradidoCurrentBalance(0), mCryptoKey(nullptr), mReferenceCount(1)
|
||||
{
|
||||
memset(mPublicKey, 0, crypto_sign_PUBLICKEYBYTES);
|
||||
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(mLanguage);
|
||||
}
|
||||
// load from db
|
||||
User::User(const char* email)
|
||||
@ -210,6 +217,8 @@ User::User(const char* email)
|
||||
if (result == 1) {
|
||||
mState = USER_LOADED_FROM_DB;
|
||||
mLanguage = LanguageManager::languageFromString(language_key);
|
||||
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(mLanguage);
|
||||
|
||||
if (email_checked == 0) { mState = USER_EMAIL_NOT_ACTIVATED;}
|
||||
else if (pubkey.isNull()) { mState = USER_NO_KEYS;}
|
||||
else if (privkey.isNull()) { mState = USER_NO_PRIVATE_KEY; }
|
||||
@ -269,6 +278,8 @@ User::User(int user_id)
|
||||
if (result == 1) {
|
||||
mState = USER_LOADED_FROM_DB;
|
||||
mLanguage = LanguageManager::languageFromString(language_key);
|
||||
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(mLanguage);
|
||||
|
||||
if (email_checked == 0) { mState = USER_EMAIL_NOT_ACTIVATED; }
|
||||
else if (pubkey.isNull()) { mState = USER_NO_KEYS; }
|
||||
else if (privkey.isNull()) { mState = USER_NO_PRIVATE_KEY; }
|
||||
@ -330,6 +341,8 @@ User::User(const unsigned char* pubkey_array)
|
||||
if (result == 1) {
|
||||
mState = USER_LOADED_FROM_DB;
|
||||
mLanguage = LanguageManager::languageFromString(language_key);
|
||||
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(mLanguage);
|
||||
|
||||
if (email_checked == 0) { mState = USER_EMAIL_NOT_ACTIVATED; }
|
||||
else if (privkey.isNull()) { mState = USER_NO_PRIVATE_KEY; }
|
||||
else { mState = USER_COMPLETE; }
|
||||
@ -378,25 +391,59 @@ User::~User()
|
||||
}
|
||||
}
|
||||
|
||||
void User::setLanguage(Languages lang)
|
||||
{
|
||||
lock("User::setLanguage");
|
||||
if (mLanguage != lang) {
|
||||
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(lang);
|
||||
}
|
||||
mLanguage = lang;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
std::string User::generateNewPassphrase(Mnemonic* word_source)
|
||||
{
|
||||
auto em = ErrorManager::getInstance();
|
||||
static const char* errorMessageForUser = "Ein Fehler, bitte wende dich an den Server-Admin (coin@gradido.net). | An error occured, please ask the server admin (coin@gradido.net).";
|
||||
unsigned int random_indices[PHRASE_WORD_COUNT];
|
||||
unsigned int str_sizes[PHRASE_WORD_COUNT];
|
||||
unsigned int phrase_buffer_size = 0;
|
||||
bool errorReloadingMnemonicWordList = false;
|
||||
int loopTrys = 0;
|
||||
Poco::RegularExpression checkValidWord("^[a-zäöüß]*$");
|
||||
|
||||
// TODO: make sure words didn't double
|
||||
for (int i = 0; i < PHRASE_WORD_COUNT; i++) {
|
||||
random_indices[i] = randombytes_random() % 2048;
|
||||
auto word = word_source->getWord(random_indices[i]);
|
||||
if (loopTrys > 10 || errorReloadingMnemonicWordList) {
|
||||
return errorMessageForUser;
|
||||
}
|
||||
if (!word) {
|
||||
em->addError(new ParamError("User::generateNewPassphrase", "empty word get for index", random_indices[i]));
|
||||
em->sendErrorsAsEmail();
|
||||
|
||||
random_indices[i] = randombytes_random() % 2048;
|
||||
word = word_source->getWord(random_indices[i]);
|
||||
if (!word) return "Ein Fehler, bitte wende dich an den Server-Admin.";
|
||||
if (!word) return errorMessageForUser;
|
||||
|
||||
}
|
||||
else {
|
||||
if (!checkValidWord.match(word, 0, Poco::RegularExpression::RE_NOTEMPTY)) {
|
||||
em->addError(new ParamError("User::generateNewPassphrase", "invalid word", word));
|
||||
em->addError(new Error("User::generateNewPassphrase", "try to reload mnemonic word list, but this error is maybe evidence for a serious memory problem!!!"));
|
||||
if (!ServerConfig::loadMnemonicWordLists()) {
|
||||
em->addError(new Error("User::generateNewPassphrase", "error reloading mnemonic word lists"));
|
||||
errorReloadingMnemonicWordList = true;
|
||||
}
|
||||
else {
|
||||
i = 0;
|
||||
loopTrys++;
|
||||
}
|
||||
em->sendErrorsAsEmail();
|
||||
//return "Server Fehler, bitte frage den Admin coin@gradido.net | Server error, please ask the admin coin@gradido.net";
|
||||
}
|
||||
}
|
||||
str_sizes[i] = strlen(word);
|
||||
phrase_buffer_size += str_sizes[i];
|
||||
@ -423,7 +470,7 @@ bool User::validatePassphrase(const std::string& passphrase)
|
||||
std::vector<std::string> results(std::istream_iterator<std::string>{iss},
|
||||
std::istream_iterator<std::string>());
|
||||
for (int i = 0; i < ServerConfig::Mnemonic_Types::MNEMONIC_MAX; i++) {
|
||||
auto m = ServerConfig::g_Mnemonic_WordLists[i];
|
||||
Mnemonic& m = ServerConfig::g_Mnemonic_WordLists[i];
|
||||
bool existAll = true;
|
||||
for (auto it = results.begin(); it != results.end(); it++) {
|
||||
if (!m.isWordExist(*it)) {
|
||||
|
||||
@ -90,9 +90,11 @@ public:
|
||||
inline void setPublicKeyHex(const std::string& publicKeyHex) { lock(); mPublicHex = publicKeyHex; unlock(); }
|
||||
inline void setPublicKey(const unsigned char* key) { lock(); memcpy(mPublicKey, key, crypto_sign_PUBLICKEYBYTES); unlock();}
|
||||
|
||||
inline const char* gettext(const char* text) { if (mLanguageCatalog.isNull()) return text; return mLanguageCatalog->gettext(text); }
|
||||
|
||||
UserStates getUserState();
|
||||
|
||||
void setLanguage(Languages lang) { lock(); mLanguage = lang; unlock(); }
|
||||
void setLanguage(Languages lang);
|
||||
inline void setBalance(int balance) { lock(); mGradidoCurrentBalance = balance; unlock(); }
|
||||
void setEmailChecked();
|
||||
bool isEmptyPassword();
|
||||
@ -154,6 +156,7 @@ private:
|
||||
// ************************ DB FIELDS END ******************************
|
||||
|
||||
int mGradidoCurrentBalance;
|
||||
Poco::AutoPtr<LanguageCatalog> mLanguageCatalog;
|
||||
|
||||
// crypto key as obfus array
|
||||
// only in memory, if user has typed in password
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user