From 470b71714d1b7f852d166c3d222874bc8bd23a31 Mon Sep 17 00:00:00 2001 From: Patrice Vignola Date: Tue, 31 Jan 2023 22:36:33 -0800 Subject: [PATCH] Add stdin entropy (#35) --- README.md | 8 +--- include/arg_parser.hpp | 11 ++++- include/secret_generator.hpp | 5 +-- src/arg_parser.cpp | 34 ++++++++++++--- src/arg_parser_test.cpp | 78 ++++++++++++++--------------------- src/secret_generator_test.cpp | 4 +- 6 files changed, 75 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index ec61c4a..e71982b 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Multiple clients for the Go, Rust and Java languages already exist, but I haven' ## Usage 1. Download the executable from the [releases page](https://github.com/PatriceVignola/cpp-kzg-ceremony-client/releases) -2. Run the exeutable: `./cpp-kzg-ceremony-client --entropy=` +2. Run the exeutable: `./cpp-kzg-ceremony-client` +3. Write your entropy sentence and press enter More command-line options are also available: @@ -58,11 +59,6 @@ Usage: https://seq.ceremony.ethereum.org) -a, --auth arg Authentication provider to use. Choices: [ethereum, github] (default: ethereum) - -e, --entropy arg A phrase to initialize the entropy. Can be any - length, but will be truncated or padded to 256 - characters depending on the length. This phrase will - be fed more randomness before a secret is generated, - so it does not already need to random. -n, --no-signing Disable the signing of the contribution. Although signing contributions is not mandatory, it is recommended to verify that the contributions listed diff --git a/include/arg_parser.hpp b/include/arg_parser.hpp index 25feb05..92592aa 100644 --- a/include/arg_parser.hpp +++ b/include/arg_parser.hpp @@ -2,12 +2,19 @@ #define ARG_PARSER_HPP #include +#include enum class AuthProvider { Ethereum, GitHub, }; +enum class EntropyType { + Stdin, + Drand, + Https, +}; + class ArgParser { public: ArgParser(int argc, const char* const* argv); @@ -15,16 +22,16 @@ class ArgParser { AuthProvider get_auth_provider() const { return auth_provider_; } bool get_help_wanted() const { return help_wanted_; } const std::string& get_help_message() const { return help_message_; }; - const std::string& get_entropy() const { return entropy_; } bool signing_disabled() const { return signing_disabled_; } + std::vector get_entropy() const; private: AuthProvider auth_provider_; std::string sequencer_url_; std::string help_message_; - std::string entropy_; bool signing_disabled_; bool help_wanted_; + EntropyType entropy_type_; }; #endif // ARG_PARSER_HPP \ No newline at end of file diff --git a/include/secret_generator.hpp b/include/secret_generator.hpp index 522ec1e..e49bb7e 100644 --- a/include/secret_generator.hpp +++ b/include/secret_generator.hpp @@ -12,13 +12,12 @@ template class SecretGenerator { public: - SecretGenerator(const std::string& entropy_string, size_t num_secrets, + SecretGenerator(const std::vector& entropy, size_t num_secrets, csprng&& generator = duthomhas::csprng(), BlstSecretKey&& blst_secret_key = BlstSecretKey()) { while (secrets_.size() < num_secrets) { static constexpr size_t min_entropy_bytes = 256; - std::vector entropy_bytes(entropy_string.begin(), - entropy_string.end()); + std::vector entropy_bytes = entropy; // Replace at least half the initial+padded bytes with random ones const size_t num_random_bytes = std::max( diff --git a/src/arg_parser.cpp b/src/arg_parser.cpp index 8eb1816..97ca4e5 100644 --- a/src/arg_parser.cpp +++ b/src/arg_parser.cpp @@ -22,11 +22,9 @@ ArgParser::ArgParser(int argc, const char* const* argv) { options.add_option( "", "e", "entropy", - "A phrase to initialize the entropy. Can be any length, but will " - "be truncated or padded to 256 characters depending on the length. " - "This phrase will be fed more randomness before a secret is " - "generated, so it does not already need to random.", - cxxopts::value(), ""); + "Type of entropy to use for the first layer. Additional CSPRNG entropy " + "will be applied on top of it. Choices: [stdin]", + cxxopts::value()->default_value("stdin"), ""); options.add_option( "", "n", "no-signing", @@ -45,7 +43,6 @@ ArgParser::ArgParser(int argc, const char* const* argv) { help_wanted_ = parse_result.count("help") > 0; if (!help_wanted_) { sequencer_url_ = parse_result["sequencer"].as(); - entropy_ = parse_result["entropy"].as(); signing_disabled_ = parse_result["no-signing"].as(); } @@ -60,6 +57,15 @@ ArgParser::ArgParser(int argc, const char* const* argv) { << "`"; throw std::runtime_error(error_stream.str()); } + + const auto entropy_type = parse_result["entropy"].as(); + if (entropy_type == "stdin") { + entropy_type_ = EntropyType::Stdin; + } else { + std::stringstream error_stream; + error_stream << "invalid entropy type `" << entropy_type << "`"; + throw std::runtime_error(error_stream.str()); + } } catch (const cxxopts::option_not_exists_exception& ex) { std::stringstream error_stream; error_stream << "error when parsing arguments: " << ex.what() << std::endl @@ -67,3 +73,19 @@ ArgParser::ArgParser(int argc, const char* const* argv) { throw std::runtime_error(error_stream.str()); } } + +static std::vector get_stdin_entropy() { + std::cout << "Write a sentence to generate entropy: " << std::endl; + std::string entropy; + std::cin >> entropy; + return {entropy.begin(), entropy.end()}; +} + +std::vector ArgParser::get_entropy() const { + switch (entropy_type_) { + case EntropyType::Stdin: + return get_stdin_entropy(); + default: + throw std::runtime_error("Invalid entropy type"); + } +} diff --git a/src/arg_parser_test.cpp b/src/arg_parser_test.cpp index 99488bc..79dc56c 100644 --- a/src/arg_parser_test.cpp +++ b/src/arg_parser_test.cpp @@ -13,11 +13,9 @@ static constexpr const char* usage_message = R"( https://seq.ceremony.ethereum.org) -a, --auth arg Authentication provider to use. Choices: [ethereum, github] (default: ethereum) - -e, --entropy arg A phrase to initialize the entropy. Can be any - length, but will be truncated or padded to 256 - characters depending on the length. This phrase will - be fed more randomness before a secret is generated, - so it does not already need to random. + -e, --entropy arg Type of entropy to use for the first layer. + Additional CSPRNG entropy will be applied on top of + it. Choices: [stdin] (default: stdin) -n, --no-signing Disable the signing of the contribution. Although signing contributions is not mandatory, it is recommended to verify that the contributions listed @@ -28,10 +26,9 @@ static constexpr const char* usage_message = R"( // NOLINTNEXTLINE TEST(TestArgParser, ThrowsErrorUnknownOption) { - const std::array args = { + const std::array args = { "foo", "--bar", - "--entropy=foo", }; std::stringstream expected_error; @@ -57,10 +54,9 @@ TEST(TestArgParser, ThrowsErrorUnknownOption) { // NOLINTNEXTLINE TEST(TestArgParser, ThrowsErrorInvalidAuthenticationProvider) { - const std::array args = { + const std::array args = { "foo", "--auth=bar", - "--entropy=foo", }; // NOLINTNEXTLINE @@ -77,44 +73,40 @@ TEST(TestArgParser, ThrowsErrorInvalidAuthenticationProvider) { } // NOLINTNEXTLINE -TEST(TestArgParser, ThrowsExceptionShortArgsEqualDelimiter) { - const std::array args = { +TEST(TestArgParser, ThrowsErrorInvalidEntropyType) { + const std::array args = { "foo", - "-s=bar_sequencer", - "--entropy=foo", + "--entropy=bar", }; -#ifdef _WIN32 - const auto* expected_error = - "Argument '-s=bar_sequencer' starts with a - but has " - "incorrect syntax"; -#else - const auto* expected_error = - "Argument ‘-s=bar_sequencer’ starts with a - but has " - "incorrect syntax"; -#endif - // NOLINTNEXTLINE EXPECT_THROW( { try { ArgParser arg_parser(args.size(), args.data()); - } catch (const cxxopts::option_syntax_exception& error) { - EXPECT_STREQ(expected_error, error.what()); + } catch (const std::runtime_error& error) { + EXPECT_STREQ("invalid entropy type `bar`", error.what()); throw; } }, - cxxopts::option_syntax_exception); + std::runtime_error); } // NOLINTNEXTLINE -TEST(TestArgParser, ThrowsExceptionMissingEntropy) { - const std::array args = {"foo"}; +TEST(TestArgParser, ThrowsExceptionShortArgsEqualDelimiter) { + const std::array args = { + "foo", + "-s=bar_sequencer", + }; #ifdef _WIN32 - const auto* expected_error = "Option 'entropy' has no value"; + const auto* expected_error = + "Argument '-s=bar_sequencer' starts with a - but has " + "incorrect syntax"; #else - const auto* expected_error = "Option ‘entropy’ has no value"; + const auto* expected_error = + "Argument ‘-s=bar_sequencer’ starts with a - but has " + "incorrect syntax"; #endif // NOLINTNEXTLINE @@ -122,12 +114,12 @@ TEST(TestArgParser, ThrowsExceptionMissingEntropy) { { try { ArgParser arg_parser(args.size(), args.data()); - } catch (const cxxopts::option_has_no_value_exception& error) { + } catch (const cxxopts::option_syntax_exception& error) { EXPECT_STREQ(expected_error, error.what()); throw; } }, - cxxopts::option_has_no_value_exception); + cxxopts::option_syntax_exception); } // NOLINTNEXTLINE @@ -144,10 +136,9 @@ TEST(TestArgParser, HelpRequested) { // NOLINTNEXTLINE TEST(TestArgParser, ValidDefaultArgs) { - const std::array args = {"foo", "--entropy=bar"}; + const std::array args = {"foo"}; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::Ethereum, arg_parser.get_auth_provider()); - EXPECT_EQ("bar", arg_parser.get_entropy()); EXPECT_EQ("https://seq.ceremony.ethereum.org", arg_parser.get_sequencer_url()); EXPECT_FALSE(arg_parser.get_help_wanted()); @@ -155,54 +146,49 @@ TEST(TestArgParser, ValidDefaultArgs) { // NOLINTNEXTLINE TEST(TestArgParser, ValidArgsEqualDelimiter) { - const std::array args = { + const std::array args = { "foo", "--sequencer=foo_sequencer", "--auth=github", - "--entropy=bar", }; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::GitHub, arg_parser.get_auth_provider()); EXPECT_EQ("foo_sequencer", arg_parser.get_sequencer_url()); - EXPECT_EQ("bar", arg_parser.get_entropy()); EXPECT_FALSE(arg_parser.get_help_wanted()); } // NOLINTNEXTLINE TEST(TestArgParser, ValidArgsSpaceDelimiter) { - const std::array args = { - "foo", "--sequencer", "foo_sequencer", "--auth", - "github", "--entropy", "bar", + const std::array args = { + "foo", "--sequencer", "foo_sequencer", "--auth", "github", }; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::GitHub, arg_parser.get_auth_provider()); EXPECT_EQ("foo_sequencer", arg_parser.get_sequencer_url()); - EXPECT_EQ("bar", arg_parser.get_entropy()); EXPECT_FALSE(arg_parser.get_help_wanted()); } // NOLINTNEXTLINE TEST(TestArgParser, ValidShortArgsSpaceDelimiter) { - const std::array args = { - "foo", "-s", "foo_sequencer", "-a", "github", "-e", "bar", + const std::array args = { + "foo", "-s", "foo_sequencer", "-a", "github", }; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::GitHub, arg_parser.get_auth_provider()); EXPECT_EQ("foo_sequencer", arg_parser.get_sequencer_url()); - EXPECT_EQ("bar", arg_parser.get_entropy()); EXPECT_FALSE(arg_parser.get_help_wanted()); } // NOLINTNEXTLINE TEST(TestArgParser, GithubAuthenticationProvider) { - const std::array args = {"foo", "--entropy=bar"}; + const std::array args = {"foo"}; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::Ethereum, arg_parser.get_auth_provider()); } // NOLINTNEXTLINE TEST(TestArgParser, EthereumAuthenticationProvider) { - const std::array args = {"foo", "--entropy=bar"}; + const std::array args = {"foo"}; ArgParser arg_parser(args.size(), args.data()); EXPECT_EQ(AuthProvider::Ethereum, arg_parser.get_auth_provider()); } diff --git a/src/secret_generator_test.cpp b/src/secret_generator_test.cpp index 00dc83f..17cb829 100644 --- a/src/secret_generator_test.cpp +++ b/src/secret_generator_test.cpp @@ -26,7 +26,7 @@ TEST(TestSecretGenerator, GeneratesNothingWhenZeroSecrets) { MockCsprng csprng; EXPECT_CALL(csprng, parentheses_op(::testing::_, ::testing::_)).Times(0); - SecretGenerator secret_generator("", 0, std::move(csprng)); + SecretGenerator secret_generator({}, 0, std::move(csprng)); EXPECT_EQ(0, secret_generator.get_secrets().size()); } @@ -61,7 +61,7 @@ TEST(TestSecretGenerator, Generates256RandomBytesWhenEmptyEntropy) { expected_secret_bytes.size()); SecretGenerator secret_generator( - "", 1, std::move(csprng), std::move(blst_secret_key)); + {}, 1, std::move(csprng), std::move(blst_secret_key)); const auto& secrets = secret_generator.get_secrets(); EXPECT_EQ(1, secret_generator.get_secrets().size());