Skip to content

Commit

Permalink
Update seed command to support electrum mnemonics with 132 bits of
Browse files Browse the repository at this point in the history
entropy (the default for electrum 2.x+) or more
  • Loading branch information
thecodefactory committed Feb 10, 2018
1 parent 54df66a commit 919732d
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 12 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ console/bx
/release/bx
/release/bx.exe
*.zpl

build-libbitcoin-explorer
libbitcoin_explorer_test_runner.sh.log
libbitcoin_explorer_test_runner.sh.trs
test-suite.log
test.log
test/libbitcoin_explorer_test
28 changes: 26 additions & 2 deletions include/bitcoin/explorer/commands/seed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,12 @@ class BCX_API seed
(
"bit_length,b",
value<uint16_t>(&option_.bit_length)->default_value(192),
"The length of the seed in bits. Must be divisible by 8 and must not be less than 128, defaults to 192."
"The length of the seed in bits. Unless electrum option is used, this must be divisible by 8 and must not be less than 128, defaults to 192."
)
(
"electrum,e",
value<bool>(&option_.electrum)->zero_tokens(),
"Generate electrum compatible seed for electrum mnemonics."
);

return options;
Expand Down Expand Up @@ -187,6 +192,23 @@ class BCX_API seed
option_.bit_length = value;
}

/**
* Get the value of the electrum option.
*/
virtual bool& get_electrum_option()
{
return option_.electrum;
}

/**
* Set the value of the electrum option.
*/
virtual void set_electrum_option(
const bool& value)
{
option_.electrum = value;
}

private:

/**
Expand All @@ -210,11 +232,13 @@ class BCX_API seed
struct option
{
option()
: bit_length()
: bit_length(),
electrum()
{
}

uint16_t bit_length;
bool electrum;
} option_;
};

Expand Down
14 changes: 14 additions & 0 deletions include/bitcoin/explorer/define.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ BC_CONSTEXPR size_t minimum_seed_bits = 128;
*/
BC_CONSTEXPR size_t minimum_seed_size = minimum_seed_bits / bc::byte_bits;

/**
* The minimum safe length of an electrum seed in bits.
*/
BC_CONSTEXPR size_t minimum_electrum_seed_bits = 132;

/**
* The maximum length of an electrum seed in bits.
*
* This constant defines the arbitrarily chosen maximum allowable seed
* length, as a fixed number is required for this implementation.
* This is safe to increase/decrease as needed.
*/
BC_CONSTEXPR size_t maximum_electrum_seed_bits = 1024;

/**
* Suppported output encoding engines.
*/
Expand Down
7 changes: 7 additions & 0 deletions include/bitcoin/explorer/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ BCX_API ec_secret new_key(const data_chunk& seed);
*/
BCX_API data_chunk new_seed(size_t bit_length=minimum_seed_bits);

/**
* Generate a new pseudorandom seed suitable for electrum mnemonics.
* @param[in] seed The electrum minimum seed length in bits.
* @return The new key.
*/
BCX_API data_chunk new_electrum_seed(size_t bit_length);

/**
* Convert a list of indexes to a list of strings. This could be generalized.
* @param[in] indexes The list of indexes to convert.
Expand Down
3 changes: 2 additions & 1 deletion model/generate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,8 @@
</command>

<command symbol="seed" output="base16" category="WALLET" description="Generate a pseudorandom seed.">
<option name="bit_length" type="uint16_t" default="192" description="The length of the seed in bits. Must be divisible by 8 and must not be less than 128, defaults to 192." />
<option name="bit_length" type="uint16_t" default="192" description="The length of the seed in bits. Unless electrum option is used, this must be divisible by 8 and must not be less than 128, defaults to 192." />
<option name="electrum" description="Generate electrum compatible seed for electrum mnemonics." />
<define name="BX_SEED_BIT_LENGTH_UNSUPPORTED" value="The seed size is not supported." />
</command>

Expand Down
41 changes: 32 additions & 9 deletions src/commands/seed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
#include <bitcoin/explorer/commands/seed.hpp>

#include <iostream>
#include <bitcoin/bitcoin.hpp>
#include <bitcoin/explorer/define.hpp>
#include <bitcoin/explorer/utility.hpp>
Expand All @@ -31,19 +30,43 @@ using namespace bc::config;
console_result seed::invoke(std::ostream& output, std::ostream& error)
{
const auto bit_length = get_bit_length_option();
const auto electrum = get_electrum_option();

// These are soft requirements for security and rationality.
// We use bit vs. byte length input as the more familiar convention.
if (bit_length < minimum_seed_size * byte_bits ||
bit_length % byte_bits != 0)
if (electrum)
{
error << BX_SEED_BIT_LENGTH_UNSUPPORTED << std::endl;
return console_result::failure;
// We use bits instead of bytes since the input bit length is
// not required to be byte aligned
if (bit_length < minimum_electrum_seed_bits ||
bit_length > maximum_electrum_seed_bits)
{
error << BX_SEED_BIT_LENGTH_UNSUPPORTED << std::endl;
return console_result::failure;
}

static constexpr auto bits_per_word =
std::log2(bc::wallet::dictionary_size);

const auto number_of_entropy_bits = static_cast<uint32_t>(
std::ceil(bit_length / bits_per_word) * bits_per_word);

const auto seed = new_electrum_seed(number_of_entropy_bits);
output << base16(seed) << std::endl;
}
else
{
// These are soft requirements for security and rationality.
// We use bit vs. byte length input as the more familiar convention.
if (bit_length < minimum_seed_size * byte_bits ||
bit_length % byte_bits != 0)
{
error << BX_SEED_BIT_LENGTH_UNSUPPORTED << std::endl;
return console_result::failure;
}

const auto seed = new_seed(bit_length);
const auto seed = new_seed(bit_length);
output << base16(seed) << std::endl;
}

output << base16(seed) << std::endl;
return console_result::okay;
}

Expand Down
29 changes: 29 additions & 0 deletions src/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <iostream>
#include <random>
#include <cstdint>
#include <chrono>
#include <mutex>
#include <string>
#include <thread>
Expand All @@ -34,15 +35,23 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/random.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/random/random_device.hpp>
#include <boost/random.hpp>

#include <bitcoin/client.hpp>
#include <bitcoin/explorer/command.hpp>
#include <bitcoin/explorer/define.hpp>

using namespace std::chrono;
using namespace bc::client;
using namespace boost::random;
using namespace boost::multiprecision;
using boost::filesystem::path;

namespace libbitcoin {
Expand Down Expand Up @@ -80,6 +89,26 @@ data_chunk new_seed(size_t bit_length)
return seed;
}

data_chunk new_electrum_seed(size_t bit_length)
{
static const auto now = high_resolution_clock::now();
static mt19937 twister(now.time_since_epoch().count());

const cpp_int maximum = boost::multiprecision::pow(cpp_int(2), bit_length);
uniform_int_distribution<cpp_int> distribution(cpp_int(1), maximum);

std::stringstream sstream;
sstream << std::hex << distribution(twister);

// pre-pend zero if the string conversion length is not even
const auto prefix = ((sstream.str().size() % 2 == 0) ? "" : "0");
const auto entropy_string = prefix + sstream.str();

data_chunk seed;
decode_base16(seed, entropy_string);
return seed;
}

string_list numbers_to_strings(const chain::point::indexes& indexes)
{
string_list stringlist;
Expand Down

0 comments on commit 919732d

Please sign in to comment.