// Copyright (c) 2016-2024 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html #include "wallet/mnemonic.h" #include "random.h" #include "support/cleanse.h" #include #include extern "C" { #include "crypto/bip39/bip39.h" } // The vendored BIP39 library references random_buffer() (used by its // mnemonic_generate()). We do not compile trezor's insecure rand.c; instead we // route it to the node CSPRNG so any BIP39 randomness is cryptographically // sound. random_buffer is declared weak in rand.c, so this strong definition // is the one that links. extern "C" void random_buffer(uint8_t* buf, size_t len) { GetRandBytes(buf, (int)len); } // mnemonic_from_data()/mnemonic_to_seed() use process-static scratch buffers, // so serialize all access behind one lock and copy results out immediately. static std::mutex cs_bip39; bool MnemonicIsValid(const std::string& phrase) { std::lock_guard lock(cs_bip39); return mnemonic_check(phrase.c_str()) != 0; } bool MnemonicToEntropy(const std::string& phrase, RawHDSeed& entropyOut) { std::lock_guard lock(cs_bip39); // Reject bad checksum / unknown words first. if (mnemonic_check(phrase.c_str()) == 0) { return false; } // mnemonic_to_entropy() writes 33 bytes (entropy || 1 checksum byte) and // returns the total bit count (words * 11). uint8_t buf[33]; int totalBits = mnemonic_to_entropy(phrase.c_str(), buf); if (totalBits <= 0) { return false; } int words = totalBits / 11; if (words != 12 && words != 18 && words != 24) { memory_cleanse(buf, sizeof(buf)); return false; } int entropyBytes = words * 4 / 3; // 12->16, 18->24, 24->32 entropyOut.assign(buf, buf + entropyBytes); memory_cleanse(buf, sizeof(buf)); return true; } bool EntropyToMnemonic(const RawHDSeed& entropy, std::string& phraseOut) { std::lock_guard lock(cs_bip39); const char* phrase = mnemonic_from_data(entropy.data(), (int)entropy.size()); if (phrase == nullptr) { return false; } phraseOut.assign(phrase); mnemonic_clear(); // wipe the static buffer return true; } bool Bip39SeedFromEntropy(const RawHDSeed& entropy, RawHDSeed& seed64Out) { std::lock_guard lock(cs_bip39); // Regenerate the canonical phrase from entropy (matches SDXLite's // Mnemonic::from_entropy(entropy).phrase()), then PBKDF2 with an EMPTY // passphrase to get the standard 64-byte BIP39 seed. const char* phrase = mnemonic_from_data(entropy.data(), (int)entropy.size()); if (phrase == nullptr) { return false; } uint8_t seed[64]; mnemonic_to_seed(phrase, "", seed, nullptr); mnemonic_clear(); seed64Out.assign(seed, seed + 64); memory_cleanse(seed, sizeof(seed)); return true; } bool GenerateMnemonicEntropy(int bits, RawHDSeed& entropyOut) { if (bits != 128 && bits != 160 && bits != 192 && bits != 224 && bits != 256) { return false; } entropyOut.resize(bits / 8); GetRandBytes(entropyOut.data(), (int)entropyOut.size()); return true; }