// 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 <mutex>
#include <cstring>

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<std::mutex> lock(cs_bip39);
    return mnemonic_check(phrase.c_str()) != 0;
}

bool MnemonicToEntropy(const std::string& phrase, RawHDSeed& entropyOut)
{
    std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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;
}
