/* * This file is part of the Flowee project * Copyright (C) 2021-2025 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FLOWEE_MNEMONIC_H #define FLOWEE_MNEMONIC_H #include #include #include /** * Supplies validation of the BIP39 style mnemonics * * Notice that other than validation this class is not going to be helpful to * process the actual data, check out the class HDMasterKey for that. * * A single Mnemonic instance holds a list of possible dictionaries and allows * UI validation of the text using findWord() an clearSelectedLanguage(). * If it looks like the user is done with the whole 12 or more words mnemonic * you can use the validateMnemonic() call. When this returns `Valid` we know that * the mnemonic sentence is proper and checksums pass. */ class Mnemonic { public: /** * Used by validateMnemonic() */ enum Validity { Valid, ///< A fully valid mnemonic was found. WhitespaceError, ///< please fix your whitespace before validating. IncorrectWordCount, ///< failed basic checks UnknownLanguage, ///< first word can not be found. UnknownWord, ///< word can not be found in selected dict ChecksumFailure ///< Incorrect checksum }; /** * Find a word in the dictionary, or a dictionary to fit the word. * This method will fill the \a dict on first use and fetch the * index on further usages from the existng dict. * @return the word, or -1 if not found. */ int findWord(const QString &word); /** * The reverse of findWords, return the word that is in the current dictionary at a certain index * Throws if no dictionary is selected. * @see loadLanguage */ QString wordForIndex(int index); QStringList completeWords(const QString &partialWord) const; /** * Convert a string to the raw data indicated by the mnemonic words, including the checksum byte. * * The mnemonic is a list of words, each representing 11 bits because they come from a list of words * of 2028 items. Which is 2^11. * The raw format simply takes each words 11 bits and puts them into a single vector. */ std::vector toRawData(const QString &mnemonic, Validity *error = nullptr, int *errorIndex = nullptr); /// returns the selected id of the wordlist. Or empty if nothing is selected. QString languageId() const; void clearSelectedLanguage(); /// Add a word-list and languageId. void registerWordList(const QString &id, const QString &filename); /** * Validate the mnemonic string on correctness. * This method will check the individual words actually match * a known word-list and also check the checksum build into the bip32-style * mnemonics. * @param mnemonic the actual full sentence of words. * @param[out] index-in-text of the first word not recognized, if any. * @param[out] optional out param to store a flag if this phrase is valid * as an Electrum mnemonic. This flag will only be valid * if this function returns either Valid or ChecksumFailure. */ Validity validateMnemonic(const QString &mnemonic, int &errorIndex, bool *maybeElectrum = nullptr); QString generateMnemonic(const std::vector &entropy, const QString &langId = QString("en")) const; /** * Using the previously registered word list, proceed by loading it. * This is useful to avoid guessing on validate(), but also needed * to make wordForIndex not throw. */ void loadLanguage(const QString &id); private: /** * Validate the mnemonic against the old legacy "Electrum" mnemonic format, * which uses the same word list as BIP39, but uses a different checksum * mechanism as well as a different pbkdf512 hash salt. * Note: A correct Electrum mnemonic always either turns out to be BIP39 * "valid" or has the same word list but fails the BIP39 checksum. If * neither of these criteria are met, it cannot be a valid Electrum * mnemonic. For simplicity, this helper function does not check that these * criteria are met, it simply hashes the mnemonic and checks its checksum. * @param mnemonic the actual full sentence of words. * @return true if the mnemonic validates as Electrum, false otherwise; note * that 1/256th of the time, it's possible for the same mnemonic * phrase to validate correctly as both BIP39 and Electrum * simultaneously, hence the "maybe" name for this function. */ bool maybeValidElectrumMnemonic(const QString &mnemonic) const; QMap m_wordLists; // id -> filename QString m_languageId; QStringList m_words; }; #endif