Add validator list key gen and signing

This commit is contained in:
wilsonianb
2018-06-07 13:44:49 -05:00
parent 2e93ff4b95
commit 653e6d9538
5 changed files with 396 additions and 0 deletions

79
src/Manifest.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include "Manifest.h"
#include <beast/core/detail/base64.hpp>
#include <ripple/crypto/KeyType.h>
#include <ripple/protocol/HashPrefix.h>
#include <ripple/protocol/Sign.h>
#include <ripple/protocol/SField.h>
namespace vlist
{
Manifest::Manifest (std::string const& raw) :
m_ (ripple::SerialIter (raw.data(), raw.size()), ripple::sfGeneric)
{ ; }
Manifest::Manifest (ripple::PublicKey const& master, ripple::PublicKey const& ephemeral, uint32_t seq) :
m_ (ripple::sfGeneric)
{
using namespace ripple;
m_[sfPublicKey] = master;
m_[sfSigningPubKey] = ephemeral;
m_[sfSequence] = seq;
}
bool Manifest::isValid () const
{
using namespace ripple;
// not a complete check
return
m_.isFieldPresent (sfSequence) &&
m_.isFieldPresent (sfPublicKey) &&
m_.isFieldPresent (sfSigningPubKey) &&
publicKeyType (makeSlice(m_.getFieldVL (sfPublicKey)));
}
void Manifest::signMaster (ripple::SecretKey const& master)
{
using namespace ripple;
ripple::sign (m_, HashPrefix::manifest, KeyType::ed25519, master, sfMasterSignature);
}
void Manifest::signEphemeral (ripple::SecretKey const& ephemeral)
{
using namespace ripple;
ripple::sign (m_, HashPrefix::manifest, KeyType::ed25519, ephemeral);
}
std::string Manifest::getB64() const
{
using namespace ripple;
Serializer s;
m_.add (s);
return beast::detail::base64_encode (std::string {
reinterpret_cast<const char *>(s.data()),
s.size()});
}
ripple::PublicKey Manifest::getPublicKey() const
{
using namespace ripple;
return PublicKey (makeSlice(m_.getFieldVL (sfPublicKey)));
}
Manifest
makeManifest (std::pair<ripple::PublicKey, ripple::SecretKey> const& mSecKey,
std::pair<ripple::PublicKey, ripple::SecretKey> const& ephemKey, std::uint32_t seq)
{
using namespace ripple;
Manifest m (mSecKey.first, ephemKey.first, seq);
m.signMaster (mSecKey.second);
m.signEphemeral (ephemKey.second);
return m;
}
} // vlist

40
src/Manifest.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef _W_MANIFEST_H_
#define _W_MANIFEST_H_
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
namespace vlist
{
class Manifest
{
protected:
ripple::STObject m_;
public:
Manifest (ripple::STObject const& m) : m_ (m) { ; }
Manifest () : m_ (ripple::sfGeneric) { ; }
explicit Manifest (std::string const& raw);
Manifest (ripple::PublicKey const& master, ripple::PublicKey const& ephemeral, uint32_t seq);
bool isValid () const;
void signMaster (ripple::SecretKey const& master);
void signEphemeral (ripple::SecretKey const& ephemeral);
std::string getB64() const;
ripple::PublicKey getPublicKey() const;
};
Manifest
makeManifest (std::pair<ripple::PublicKey, ripple::SecretKey> const& mSecKey,
std::pair<ripple::PublicKey, ripple::SecretKey> const& ephemKey, std::uint32_t seq);
}
#endif

112
src/utils.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include "utils.h"
#include <beast/core/detail/base64.hpp>
#include <ripple/basics/StringUtilities.h>
#include <ripple/json/to_string.h>
#include "Manifest.h"
// Will need porting to other platforms
// Used to get password/passphrase without echoing
#include <termios.h>
std::string toBase64 (std::string const& in)
{
return beast::detail::base64_encode (in);
}
boost::optional<std::string> signUNL (
ripple::SecretKey const& ephemSecKey,
std::string const& manifest,
uint32_t sequence,
uint32_t expiration,
std::vector <std::string> const& manifests)
{
using namespace ripple;
std::string data =
"{\"sequence\":" + std::to_string(sequence) +
",\"expiration\":" + std::to_string(expiration) +
",\"validators\":[";
std::string valsMsg = "Adding the following validator public keys to the list:\n";
for (auto const& manifest : manifests)
{
try
{
vlist::Manifest m (beast::detail::base64_decode(manifest));
if (! m.isValid()) {
std::cout << "Invalid manifest:" << std::endl;
std::cout << manifest << std::endl;
return boost::none;
}
auto const pubKey = m.getPublicKey();
valsMsg += toBase58(TokenType::TOKEN_NODE_PUBLIC, pubKey) + "\n";
data += "{\"validation_public_key\":\"" + strHex(pubKey) + "\","
"\"manifest\":\"" + manifest + "\"},";
}
catch (...)
{
std::cout << "Invalid manifest:" << std::endl;
std::cout << manifest << std::endl;
return boost::none;
}
}
data.pop_back();
data += "]}";
std::cout << valsMsg << std::endl;
auto pubKey = derivePublicKey (KeyType::ed25519, ephemSecKey);
Json::Value jv;
jv["blob"] = toBase64 (data);
jv["manifest"] = manifest;
jv["signature"] = strHex (sign (pubKey, ephemSecKey, makeSlice(data)));
jv["version"] = 1;
return pretty(jv);
}
// Read a character without echoing
// Will need porting to other platforms
int tc_getch()
{
struct termios t_old, t_new;
tcgetattr(0, &t_old);
t_new = t_old;
t_new.c_lflag &= ~(ICANON | ECHO);
tcsetattr(0, TCSANOW, &t_new);
auto ch = getchar();
tcsetattr(0, TCSANOW, &t_old);
return ch;
}
// Read a password, echoing *'s
ripple::SecretKey getPass()
{
std::string pass;
int ch;
while ((ch = tc_getch()) != 10)
{
if (ch == 127)
{
// backspace
if (pass.length() > 0)
{
std::cout << "\b \b" << std::flush;
pass.resize (pass.length() - 1);
}
}
else
{
pass += static_cast<unsigned char>(ch);
std::cout << '*' << std::flush;
}
}
std::cout << std::endl;
return ripple::SecretKey(ripple::makeSlice(std::move(ripple::strUnHex(pass).first)));
}

18
src/utils.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _W_UTILS_H_
#define _W_UTILS_H_
#include <string>
#include <ripple/protocol/SecretKey.h>
std::string toBase64 (std::string const& in);
boost::optional<std::string> signUNL (
ripple::SecretKey const& ephemSecKey,
std::string const& manifest,
uint32_t sequence,
uint32_t expiration,
std::vector <std::string> const& manifests);
ripple::SecretKey getPass ();
#endif

147
src/validator-list-tool.cpp Normal file
View File

@@ -0,0 +1,147 @@
#include "Manifest.h"
#include "utils.h"
#include <fstream>
#include <sys/stat.h>
#include <ripple/basics/StringUtilities.h>
int read_selection()
{
std::string line;
do
{
std::getline(std::cin, line);
if (line.empty() && std::cout.fail())
return -1;
else
return std::atoi(line.c_str());
} while(1);
}
void sign_unl ()
{
std::string manifest;
uint32_t sequence;
uint32_t expiration;
int days;
std::vector <std::string> manifests;
std::cout << std::endl << "Enter ephemeral private key:" << std::endl;
ripple::SecretKey const secretKey = getPass();
std::cout << std::endl << "Enter ephemeral key manifest:" << std::endl;
std::cin >> manifest;
std::cin.ignore();
std::cout << std::endl << "Sequence number: ";
std::cin >> sequence;
std::cin.ignore();
std::cout << std::endl << "Validity in days: ";
std::cin >> days;
std::cin.ignore();
// convert validity in days to seconds since 1/1/2000
auto now = time (NULL) - 946684800;
expiration = now - (now % 86400) + ((days + 1) * 86400);
std::cout << std::endl << "Enter validator manifests, ending with a blank line:" <<
std::endl;
while (1)
{
std::string j;
std::getline (std::cin, j);
if (j.empty())
break;
// check validity, WRITEME
manifests.push_back (j);
}
auto unl = signUNL (secretKey, manifest, sequence, expiration, manifests);
if (unl)
{
std::cout << *unl << std::endl << std::endl;
}
}
bool validator_list_operations ()
{
while (1)
{
std::cout << std::endl << std::endl << std::endl << std::endl;
printf("Validator List menu\n\n");
printf("1) Create validator list publisher keys\n\n");
printf("2) Sign validator list\n\n");
printf("9) Quit\n\n");
switch (read_selection())
{
case 1:
{
std::cout << "\nSelect a name for this credential set: ";
std::string name;
std::cin >> name;
std::cin.ignore();
std::cout << std::endl;
if (mkdir (name.c_str(), 0700) != 0)
{
std::cout << "Unable to create directory" << std::endl;
break;
}
using namespace ripple;
auto const masterKey = randomKeyPair(KeyType::ed25519);
for (std::uint32_t seq = 1; seq <= 10; ++seq)
{
auto const newKey = randomKeyPair(KeyType::ed25519);
auto manifest = vlist::makeManifest (masterKey, newKey, seq);
std::ofstream f;
f.open (name + "/ephkey" + std::to_string (seq) + ".txt");
if (! f.is_open())
{
std::cout << "Unable to open file" << std::endl;
break;
}
f << "Private Key:\n\n" <<
newKey.second.to_string() << std::endl;
f << "---------------------" << std::endl << std::endl;
f << "Manifest:\n\n" << manifest.getB64() << std::endl;
}
std::ofstream priv, pub;
priv.open ("privkeys.txt", std::fstream::app);
pub.open ("pubkeys.txt", std::fstream::app);
priv << name << " privkey: " <<
masterKey.second.to_string() << std::endl;
pub << name << " pubkey: " << strHex(masterKey.first) << std::endl;
std::cout << "Publisher keys stored in privkeys.txt and pubkeys.txt" << std::endl;
std::cout << "Ephemeral keys stored in " << name << "/" << std::endl;
break;
}
case 2:
sign_unl ();
break;
case 9:
case -1:
return false;
}
return true;
}
}
int main (int argc, char *argv[])
{
while(1)
{
if (! validator_list_operations())
break;
}
return 0;
}