update: add keygen for root keys

main
_ 2021-01-19 22:52:02 +00:00
parent 49a30866df
commit 7b11633015
7 changed files with 2277 additions and 52 deletions

View File

@ -1,2 +1,3 @@
/bmc_test
/bmc
*.key
*.o

View File

@ -5,10 +5,10 @@ CXX_FLAGS=$(FLAGS) -c
L_FLAGS=$(FLAGS)
LIBS=-lsodium
bmc_test: bmc_test.o base64.o expiring_signature.o receiver.o sender.o signing_key.o sodium_helpers.o string_helpers.o time_helpers.o
bmc: bmc_main.o base64.o expiring_signature.o receiver.o sender.o signing_key.o sodium_helpers.o string_helpers.o time_helpers.o
$(CXX) -o $@ $^ $(L_FLAGS) $(LIBS)
bmc_test.o: bmc_test.cpp expiring_signature.h receiver.h sender.h signing_key.h sodium_helpers.h string_helpers.h time_helpers.h
bmc_main.o: bmc_main.cpp expiring_signature.h receiver.h sender.h signing_key.h sodium_helpers.h string_helpers.h time_helpers.h
$(CXX) -o $@ $(CXX_FLAGS) $<
base64.o: cpp-base64/base64.cpp cpp-base64/base64.h
@ -36,5 +36,5 @@ time_helpers.o: time_helpers.cpp time_helpers.h
$(CXX) -o $@ $(CXX_FLAGS) $<
clean:
rm -f bmc_test bmc_test.o base64.o expiring_signature.o receiver.o sender.o signing_key.o sodium_helpers.o string_helpers.o time_helpers.o
rm -f bmc bmc_main.o base64.o expiring_signature.o receiver.o sender.o signing_key.o sodium_helpers.o string_helpers.o time_helpers.o

View File

@ -5,6 +5,7 @@
#include <string>
#include <vector>
#include "cxxopts.hpp"
#include "json.hpp"
#include "expiring_signature.h"
@ -19,46 +20,14 @@ using namespace std;
using nlohmann::json;
using namespace BareMinimumCrypto;
string get_passphrase_from_user () {
// In prod this would NOT be hard-coded.
return "Correct Horse Battery Staple";
}
int happy_path () {
int test () {
if (test_base64 () != 0) {
return 1;
}
// We generate a root key and keep it somewhere safe
// (offline, hopefully)
// Passphrases are mandatory for root keys, and BMC also generates
// a salt to maximize entropy.
const auto passphrase = get_passphrase_from_user ();
vector <uint8_t> seed;
seed.resize (crypto_sign_SEEDBYTES);
vector <uint8_t> salt;
salt.resize (crypto_pwhash_SALTBYTES);
randombytes_buf (salt.data (), salt.size ());
if (crypto_pwhash (
seed.data (), seed.size (),
passphrase.data (), passphrase.size (),
salt.data (),
crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
) != 0) {
return 1;
}
vector <uint8_t> pk;
pk.resize (crypto_sign_PUBLICKEYBYTES);
vector <uint8_t> sk;
sk.resize (crypto_sign_SECRETKEYBYTES);
if (crypto_sign_seed_keypair (pk.data (), sk.data (), seed.data ()) != 0) {
return 1;
}
cerr << "Passphrased root pub key " << base64_encode (pk) << endl;
SigningKey root_key;
cerr << "Root pub key " << base64_encode (root_key.pubkey ()) << endl;
@ -99,15 +68,39 @@ int happy_path () {
return 0;
}
int main () {
if (test_base64 () != 0) {
return 1;
}
int main (int argc, char ** argv) {
cxxopts::Options options ("BareMinimumCrypto", "Simple crypto things you might need.");
options.add_options ()
("generate-ca-key", "Generate a passphrase-protected certificate authority key", cxxopts::value <string> ())
("check-ca-key", "Read information from a CA key without decrypting it")
("test", "Run self-test")
("help", "Print usage")
;
if (happy_path () != 0) {
return 1;
}
auto result = options.parse (argc, argv);
cerr << "All good." << endl;
if (result.count ("help")) {
cout << options.help () << endl;
}
else if (result.count ("generate-ca-key")) {
const auto file_path = result ["generate-ca-key"].as <string> ();
cout << "Type passphrase (it will be visible in the console)" << endl;
string passphrase;
cin >> passphrase;
auto key_opt = SigningKey::generate_to_file (file_path, passphrase);
if (! key_opt) {
cerr << "Error. Key was not generated" << endl;
return 1;
}
cout << "The pubkey is `" << base64_encode (key_opt->pubkey ()) << "`" << endl;
}
else if (result.count ("test")) {
if (test () != 0) {
return 1;
}
cerr << "All good." << endl;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
#include "signing_key.h"
#include <fstream>
#include "json.hpp"
#include "sodium_helpers.h"
@ -7,6 +9,62 @@
namespace BareMinimumCrypto {
using nlohmann::json;
// The whole process for a passphrased key is like this:
// Passphrase + random salt -->
// Seed -->
// Secret key + Public key
// Passphrases should be mandatory for keys that can sign other keys.
optional <SigningKey> SigningKey::generate_to_file (const string & file_path, const string & passphrase) {
if (passphrase.size () < 8) {
return nullopt;
}
vector <uint8_t> seed;
seed.resize (crypto_sign_SEEDBYTES);
vector <uint8_t> salt;
salt.resize (crypto_pwhash_SALTBYTES);
randombytes_buf (salt.data (), salt.size ());
if (crypto_pwhash (
seed.data (), seed.size (),
passphrase.data (), passphrase.size (),
salt.data (),
crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
) != 0) {
return nullopt;
}
SigningKey key;
key.pk.resize (crypto_sign_PUBLICKEYBYTES);
key.sk.resize (crypto_sign_SECRETKEYBYTES);
if (crypto_sign_seed_keypair (key.pk.data (), key.sk.data (), seed.data ()) != 0) {
return nullopt;
}
const auto j = json {
{"salt", json::binary (salt)},
{"time_created", Instant::now ().x},
{"pubkey", json::binary (key.pk)},
};
const auto msg = json::to_msgpack (j);
ofstream f;
f.open (file_path, ofstream::binary);
if (! f.is_open ()) {
return nullopt;
}
f.write ((const char *)msg.data (), msg.size ());
f.close ();
return key;
}
SigningKey::SigningKey () {
try_sodium_init ();

View File

@ -2,6 +2,7 @@
#include <optional>
#include <stdint.h>
#include <string>
#include <vector>
#include "expiring_signature.h"
@ -10,14 +11,19 @@
namespace BareMinimumCrypto {
using namespace std;
class SigningKey {
struct SigningKey {
vector <uint8_t> pk;
vector <uint8_t> sk;
public:
SigningKey ();
//static optional <SigningKey> generate_to_file
// This doesn't fsync, so it's possible to lose the key due to a power outage
// or filesystem nonsense right after this function returns.
// It also doesn't do the rename trick. The caller may do that.
static optional <SigningKey> generate_to_file (const string & file_path, const string & passphrase);
static optional <SigningKey> load_from_file (const string & file_path, const string & passphrase);
vector <uint8_t> pubkey () const;
vector <uint8_t> pub_to_msgpack () const;

View File

@ -0,0 +1 @@
- Use libsodium's secure memory when handling keys / seeds / passphrases