Add validator list key gen and signing
This commit is contained in:
79
src/Manifest.cpp
Normal file
79
src/Manifest.cpp
Normal 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
40
src/Manifest.h
Normal 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
112
src/utils.cpp
Normal 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
18
src/utils.h
Normal 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
147
src/validator-list-tool.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user