Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Merge branch 'devel' into task/ebruck/KEP-615
Browse files Browse the repository at this point in the history
  • Loading branch information
ebruck committed Sep 18, 2018
2 parents b80439c + 4dbd5ea commit 4167ba6
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ scripts/database_pb2.py
scripts/audit_pb2.py
scripts/pbft_pb2.py
*.pyc
*.pem
2 changes: 1 addition & 1 deletion options/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ add_library(options STATIC
simple_options.hpp
)

target_link_libraries(options)
target_link_libraries(options utils)
target_include_directories(options PRIVATE ${JSONCPP_INCLUDE_DIRS})
add_dependencies(options jsoncpp)

Expand Down
10 changes: 8 additions & 2 deletions options/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/lexical_cast.hpp>
#include <regex>
#include <cstdint>
#include <utils/crypto.hpp>

using namespace bzn;
using namespace bzn::option_names;
Expand Down Expand Up @@ -118,8 +119,13 @@ options::get_bootstrap_peers_url() const
bzn::uuid_t
options::get_uuid() const
{
//TODO: Remove this
return this->raw_opts.get<std::string>(NODE_UUID);
if (this->raw_opts.has(NODE_UUID))
{
return this->raw_opts.get<std::string>(NODE_UUID);
}

std::string pubkey_raw = bzn::utils::crypto::read_pem_file(this->raw_opts.get<std::string>(NODE_PUBKEY_FILE), "PUBLIC KEY");
return boost::beast::detail::base64_encode(pubkey_raw);
}

bool
Expand Down
19 changes: 18 additions & 1 deletion options/simple_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ simple_options::build_options()
po::value<std::string>()->default_value("2G"),
"maximum db storage on this node (bytes)")
(NODE_UUID.c_str(),
po::value<std::string>()->required(),
po::value<std::string>(),
"uuid of this node")
(STATE_DIR.c_str(),
po::value<std::string>()->default_value("./.state/"),
Expand Down Expand Up @@ -135,6 +135,17 @@ simple_options::build_options()
"require signed key for new peers to join swarm");
this->options_root.add(experimental);

po::options_description crypto("Cryptography");
crypto.add_options()
(NODE_PUBKEY_FILE.c_str(),
po::value<std::string>()->default_value(".state/public-key.pem"),
"public key of this node")
(NODE_PRIVATEKEY_FILE.c_str(),
po::value<std::string>()->default_value(".state/private-key.pem"),
"private key of this node");

this->options_root.add(crypto);

}

bool
Expand Down Expand Up @@ -164,6 +175,12 @@ simple_options::validate_options()
errors = true;
}

if (!this->vm[NODE_PUBKEY_FILE].defaulted() && this->has(NODE_UUID))
{
std::cerr << "You cannot specify both a uuid and a public key; public keys act as uuids";
errors = true;
}

return !errors;
}

Expand Down
2 changes: 2 additions & 0 deletions options/simple_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace bzn::option_names
const std::string MONITOR_ADDRESS = "monitor_address";
const std::string MONITOR_PORT = "monitor_port";
const std::string NODE_UUID = "uuid";
const std::string NODE_PUBKEY_FILE = "public_key_file";
const std::string NODE_PRIVATEKEY_FILE = "private_key_file";
const std::string PBFT_ENABLED = "use_pbft";
const std::string STATE_DIR = "state_dir";
const std::string WS_IDLE_TIMEOUT = "ws_idle_timeout";
Expand Down
2 changes: 1 addition & 1 deletion options/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(test_srcs options_test.cpp)
set(test_libs options)
set(test_libs options utils)

add_gmock_test(options)
74 changes: 66 additions & 8 deletions options/test/options_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <boost/lexical_cast.hpp>
#include <fstream>
#include <gtest/gtest.h>
#include <unordered_set>


namespace
Expand All @@ -28,7 +29,7 @@ namespace

const std::string TEST_CONFIG_FILE = "bluzelle.json";

const std::string DEFAULT_CONFIG_DATA = "{\n"
const std::string DEFAULT_CONFIG_CONTENT =
" \"listener_address\" : \"0.0.0.0\",\n"
" \"listener_port\" : 49152,\n"
" \"ethereum\" : \"0x006eae72077449caca91078ef78552c0cd9bce8f\",\n"
Expand All @@ -42,8 +43,15 @@ namespace
" \"logfile_max_size\" : \"1M\","
" \"logfile_rotation_size\" : \"2M\","
" \"logfile_dir\" : \".\","
" \"http_port\" : 80"
"}";
" \"http_port\" : 80";

const std::string DEFAULT_CONFIG_DATA = "{" + DEFAULT_CONFIG_CONTENT + "}";

std::string compose_config_data(const std::string& a, const std::string& b)
{
std::string result = "{" + a + ",\n" + b + "}";
return result;
}

const auto DEFAULT_LISTENER = boost::asio::ip::tcp::endpoint{boost::asio::ip::address::from_string("0.0.0.0"), 49152};

Expand All @@ -69,24 +77,45 @@ using namespace ::testing;
class options_file_test : public Test
{
public:

std::unordered_set<std::string> open_files;

options_file_test()
{
this->save_options_file(DEFAULT_CONFIG_DATA);
}

~options_file_test()
{
unlink(TEST_CONFIG_FILE.c_str());
for(const auto& file : this->open_files)
{
unlink(file.c_str());
}
}

void
save_options_file(const std::string& content)
void save_file(const std::string& filename, const std::string& content)
{
unlink(TEST_CONFIG_FILE.c_str());
std::ofstream ofile(TEST_CONFIG_FILE);
if(this->open_files.count(filename) > 0)
{
unlink(TEST_CONFIG_FILE.c_str());
}
else
{
this->open_files.insert(filename);
}

std::cout << filename;

std::ofstream ofile(filename.c_str());
ofile.exceptions(std::ios::failbit);
ofile << content;
}

void
save_options_file(const std::string& content)
{
save_file(TEST_CONFIG_FILE, content);
}
};

TEST_F(options_file_test, test_that_missing_arguments_fail)
Expand Down Expand Up @@ -269,3 +298,32 @@ TEST_F(options_file_test, test_that_endpoint_built)

EXPECT_EQ(options.get_monitor_endpoint(io_context), expect);
}

TEST_F(options_file_test, test_that_pubkey_used_for_uuid)
{
bzn::options options;
this->save_options_file("{\"public_key_file\": \"pkey.pem\"}");

this->save_file("pkey.pem",
"-----BEGIN PUBLIC KEY-----\n"
"hFWG\n"
"-----END PUBLIC KEY-----\n"
);

try
{
options.parse_command_line(1, NO_ARGS);
}
catch (const std::exception& e)
{
}

EXPECT_EQ(options.get_uuid(), "hFWG");
}

TEST_F(options_file_test, test_that_uuid_and_pubkey_conflict)
{
bzn::options options;
this->save_options_file(compose_config_data(DEFAULT_CONFIG_CONTENT, "\"public_key_file\": \"somefile\""));
EXPECT_FALSE(options.parse_command_line(1, NO_ARGS));
}
18 changes: 18 additions & 0 deletions scripts/generate-key
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -e

prefix=${1:-`pwd`/.state}

mkdir -p $prefix

# This is the elliptic curve used by Ethereum. Technically, this identity is an
# Ethereum account.
openssl ecparam -name secp256k1 -genkey -noout -out $prefix/private-key.pem
cat $prefix/private-key.pem
echo "Private key written to "$prefix"/private-key.pem"
echo

openssl ec -in $prefix/private-key.pem -pubout -out $prefix/public-key.pem > /dev/null 2>&1
cat $prefix/public-key.pem
echo "Public key written to "$prefix"/public-key.pem"
echo
31 changes: 31 additions & 0 deletions utils/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <stdexcept>
#include <iostream>
#include <memory>
#include <cstdio>

namespace
{
Expand Down Expand Up @@ -256,4 +257,34 @@ namespace bzn::utils::crypto

return ret_val & authentic;
}

std::string read_pem_file(const std::string& filename, const std::string& expected_type)
{
char* name;
char* headers;
unsigned char* data;
long len;

::FILE* fp = fopen(filename.c_str(), "r");
if( !fp)
{
throw std::runtime_error("Failed to read pem file: " + filename);
}

PEM_read(fp, &name, &headers, &data, &len);
::fclose(fp);

if (std::string(name) != expected_type)
{
throw std::runtime_error("Expedted to find a " + expected_type + " in " + filename + ", but found a " + std::string(name));
}

std::string result(reinterpret_cast<char const*>(data), len);

OPENSSL_free(name);
OPENSSL_free(data);
OPENSSL_free(headers);

return result;
}
}
10 changes: 10 additions & 0 deletions utils/crypto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ namespace bzn::utils::crypto
* failed before finishing due to some error.
*/
bool verify_signature(const std::string& public_key, const std::string& signature, const std::string& uuid);

/**
* Read the contents of a .pem file, returning the payload as a base64 string.
* Any headers are ignored.
*
* @param filename path of pem file to read
* @param expected_type what the file is supposed to contain, such as "PRIVATE KEY".
* @throws runtime_error if the file does not indicate that it contains expected_type
*/
std::string read_pem_file(const std::string& filename, const std::string& expected_type);
}

0 comments on commit 4167ba6

Please sign in to comment.