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 *.o

View File

@ -5,10 +5,10 @@ CXX_FLAGS=$(FLAGS) -c
L_FLAGS=$(FLAGS) L_FLAGS=$(FLAGS)
LIBS=-lsodium 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) $(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) $< $(CXX) -o $@ $(CXX_FLAGS) $<
base64.o: cpp-base64/base64.cpp cpp-base64/base64.h 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) $< $(CXX) -o $@ $(CXX_FLAGS) $<
clean: 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 <string>
#include <vector> #include <vector>
#include "cxxopts.hpp"
#include "json.hpp" #include "json.hpp"
#include "expiring_signature.h" #include "expiring_signature.h"
@ -19,46 +20,14 @@ using namespace std;
using nlohmann::json; using nlohmann::json;
using namespace BareMinimumCrypto; using namespace BareMinimumCrypto;
string get_passphrase_from_user () { int test () {
// In prod this would NOT be hard-coded. if (test_base64 () != 0) {
return "Correct Horse Battery Staple"; return 1;
} }
int happy_path () {
// We generate a root key and keep it somewhere safe // We generate a root key and keep it somewhere safe
// (offline, hopefully) // (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; SigningKey root_key;
cerr << "Root pub key " << base64_encode (root_key.pubkey ()) << endl; cerr << "Root pub key " << base64_encode (root_key.pubkey ()) << endl;
@ -99,15 +68,39 @@ int happy_path () {
return 0; return 0;
} }
int main () { int main (int argc, char ** argv) {
if (test_base64 () != 0) { 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")
;
auto result = options.parse (argc, argv);
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; return 1;
} }
cout << "The pubkey is `" << base64_encode (key_opt->pubkey ()) << "`" << endl;
if (happy_path () != 0) { }
else if (result.count ("test")) {
if (test () != 0) {
return 1; return 1;
} }
cerr << "All good." << endl; cerr << "All good." << endl;
}
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
#include "signing_key.h" #include "signing_key.h"
#include <fstream>
#include "json.hpp" #include "json.hpp"
#include "sodium_helpers.h" #include "sodium_helpers.h"
@ -7,6 +9,62 @@
namespace BareMinimumCrypto { namespace BareMinimumCrypto {
using nlohmann::json; 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 () { SigningKey::SigningKey () {
try_sodium_init (); try_sodium_init ();

View File

@ -2,6 +2,7 @@
#include <optional> #include <optional>
#include <stdint.h> #include <stdint.h>
#include <string>
#include <vector> #include <vector>
#include "expiring_signature.h" #include "expiring_signature.h"
@ -10,14 +11,19 @@
namespace BareMinimumCrypto { namespace BareMinimumCrypto {
using namespace std; using namespace std;
class SigningKey { struct SigningKey {
vector <uint8_t> pk; vector <uint8_t> pk;
vector <uint8_t> sk; vector <uint8_t> sk;
public:
SigningKey (); 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> pubkey () const;
vector <uint8_t> pub_to_msgpack () const; vector <uint8_t> pub_to_msgpack () const;

View File

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