Skip to content

Commit

Permalink
Add stdin entropy (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
PatriceVignola authored Feb 1, 2023
1 parent ee67b95 commit 470b717
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 65 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<sentence_of_your_choosing>`
2. Run the exeutable: `./cpp-kzg-ceremony-client`
3. Write your entropy sentence and press enter

More command-line options are also available:

Expand All @@ -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
Expand Down
11 changes: 9 additions & 2 deletions include/arg_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@
#define ARG_PARSER_HPP

#include <string>
#include <vector>

enum class AuthProvider {
Ethereum,
GitHub,
};

enum class EntropyType {
Stdin,
Drand,
Https,
};

class ArgParser {
public:
ArgParser(int argc, const char* const* argv);
const std::string& get_sequencer_url() const { return sequencer_url_; }
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<uint8_t> 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
5 changes: 2 additions & 3 deletions include/secret_generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ template <typename csprng = duthomhas::csprng,
typename BlstSecretKey = blst::SecretKey>
class SecretGenerator {
public:
SecretGenerator(const std::string& entropy_string, size_t num_secrets,
SecretGenerator(const std::vector<uint8_t>& 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<uint8_t> entropy_bytes(entropy_string.begin(),
entropy_string.end());
std::vector<uint8_t> entropy_bytes = entropy;

// Replace at least half the initial+padded bytes with random ones
const size_t num_random_bytes = std::max(
Expand Down
34 changes: 28 additions & 6 deletions src/arg_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>(), "");
"Type of entropy to use for the first layer. Additional CSPRNG entropy "
"will be applied on top of it. Choices: [stdin]",
cxxopts::value<std::string>()->default_value("stdin"), "");

options.add_option(
"", "n", "no-signing",
Expand All @@ -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<std::string>();
entropy_ = parse_result["entropy"].as<std::string>();
signing_disabled_ = parse_result["no-signing"].as<bool>();
}

Expand All @@ -60,10 +57,35 @@ ArgParser::ArgParser(int argc, const char* const* argv) {
<< "`";
throw std::runtime_error(error_stream.str());
}

const auto entropy_type = parse_result["entropy"].as<std::string>();
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
<< help_message_;
throw std::runtime_error(error_stream.str());
}
}

static std::vector<uint8_t> 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<uint8_t> ArgParser::get_entropy() const {
switch (entropy_type_) {
case EntropyType::Stdin:
return get_stdin_entropy();
default:
throw std::runtime_error("Invalid entropy type");
}
}
78 changes: 32 additions & 46 deletions src/arg_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,10 +26,9 @@ static constexpr const char* usage_message = R"(

// NOLINTNEXTLINE
TEST(TestArgParser, ThrowsErrorUnknownOption) {
const std::array<const char*, 3> args = {
const std::array<const char*, 2> args = {
"foo",
"--bar",
"--entropy=foo",
};

std::stringstream expected_error;
Expand All @@ -57,10 +54,9 @@ TEST(TestArgParser, ThrowsErrorUnknownOption) {

// NOLINTNEXTLINE
TEST(TestArgParser, ThrowsErrorInvalidAuthenticationProvider) {
const std::array<const char*, 3> args = {
const std::array<const char*, 2> args = {
"foo",
"--auth=bar",
"--entropy=foo",
};

// NOLINTNEXTLINE
Expand All @@ -77,57 +73,53 @@ TEST(TestArgParser, ThrowsErrorInvalidAuthenticationProvider) {
}

// NOLINTNEXTLINE
TEST(TestArgParser, ThrowsExceptionShortArgsEqualDelimiter) {
const std::array<const char*, 3> args = {
TEST(TestArgParser, ThrowsErrorInvalidEntropyType) {
const std::array<const char*, 2> 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<const char*, 1> args = {"foo"};
TEST(TestArgParser, ThrowsExceptionShortArgsEqualDelimiter) {
const std::array<const char*, 2> 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
EXPECT_THROW(
{
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
Expand All @@ -144,65 +136,59 @@ TEST(TestArgParser, HelpRequested) {

// NOLINTNEXTLINE
TEST(TestArgParser, ValidDefaultArgs) {
const std::array<const char*, 2> args = {"foo", "--entropy=bar"};
const std::array<const char*, 1> 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());
}

// NOLINTNEXTLINE
TEST(TestArgParser, ValidArgsEqualDelimiter) {
const std::array<const char*, 4> args = {
const std::array<const char*, 3> 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<const char*, 7> args = {
"foo", "--sequencer", "foo_sequencer", "--auth",
"github", "--entropy", "bar",
const std::array<const char*, 5> 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<const char*, 7> args = {
"foo", "-s", "foo_sequencer", "-a", "github", "-e", "bar",
const std::array<const char*, 5> 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<const char*, 2> args = {"foo", "--entropy=bar"};
const std::array<const char*, 1> 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<const char*, 2> args = {"foo", "--entropy=bar"};
const std::array<const char*, 1> args = {"foo"};
ArgParser arg_parser(args.size(), args.data());
EXPECT_EQ(AuthProvider::Ethereum, arg_parser.get_auth_provider());
}
4 changes: 2 additions & 2 deletions src/secret_generator_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ TEST(TestSecretGenerator, GeneratesNothingWhenZeroSecrets) {
MockCsprng csprng;
EXPECT_CALL(csprng, parentheses_op(::testing::_, ::testing::_)).Times(0);

SecretGenerator<MockCsprng> secret_generator("", 0, std::move(csprng));
SecretGenerator<MockCsprng> secret_generator({}, 0, std::move(csprng));
EXPECT_EQ(0, secret_generator.get_secrets().size());
}

Expand Down Expand Up @@ -61,7 +61,7 @@ TEST(TestSecretGenerator, Generates256RandomBytesWhenEmptyEntropy) {
expected_secret_bytes.size());

SecretGenerator<MockCsprng, MockBlstSecretKey> 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());
Expand Down

0 comments on commit 470b717

Please sign in to comment.