🚧 wip: working on process for a human key signing other keys

main
_ 2021-01-19 20:58:24 -06:00
parent 02346c3285
commit ddba8953fa
3 changed files with 113 additions and 44 deletions

View File

@ -25,25 +25,8 @@ namespace fs = std::filesystem;
int file (const string & file_path) {
cout << "Reading `" << file_path << "`" << endl;
ifstream f;
f.open (file_path, ifstream::binary);
if (! f.is_open ()) {
cerr << "Can't open file." << endl;
return 1;
}
f.seekg (0, ifstream::end);
const auto len = f.tellg ();
f.seekg (0, ifstream::beg);
Bytes bytes;
bytes.resize (len);
f.read ((char *)bytes.data (), bytes.size ());
// All our files are msgpack, so parse it first.
const auto j = json::from_msgpack (bytes);
const auto j = std::move (*try_load_msgpack_file (file_path));
const string schema = j ["schema"];
@ -164,13 +147,18 @@ int test () {
int main (int argc, char ** argv) {
cxxopts::Options options ("BareMinimumCrypto", "Simple crypto things you might need.");
options.add_options ()
// Commands
("help", "Print usage")
("file", "Print info about any file generated by BMC", cxxopts::value <string> ())
("generate-human-key", "Generate a passphrase-protected key for human use", cxxopts::value <string> ())
("generate-machine-key", "Generate a key for machine use, with no passphrase", cxxopts::value <string> ())
("generate-key-cert", "Certify a key for 1 month and save the cert here", cxxopts::value <string> ())
("generate-key-cert", "Certify a key for 3 months and save the cert here", cxxopts::value <string> ())
// cxxopts nonsense
("using-key", "Key to load for other operations", cxxopts::value <string> ())
("file", "Print info about any file generated by BMC", cxxopts::value <string> ())
// Other stuff
("test", "Run self-test")
("help", "Print usage")
;
auto result = options.parse (argc, argv);
@ -178,11 +166,13 @@ int main (int argc, char ** argv) {
if (result.count ("help")) {
cout << options.help () << endl;
}
else if (result.count ("file")) {
const auto file_path = result ["file"].as <string> ();
return file (file_path);
}
else if (result.count ("generate-human-key")) {
const auto file_path = result ["generate-human-key"].as <string> ();
cout << "Type passphrase (it will be visible in the console)" << endl;
string passphrase;
cin >> passphrase;
const auto passphrase = get_passphrase_from_user ();
auto key_opt = HumanKeyFile::generate (file_path, passphrase);
if (! key_opt) {
@ -201,9 +191,31 @@ int main (int argc, char ** argv) {
}
cout << "The pubkey is `" << base64_encode (key_opt->pubkey ()) << "`" << endl;
}
else if (result.count ("file")) {
const auto file_path = result ["file"].as <string> ();
return file (file_path);
else if (result.count ("generate-key-cert")) {
if (! result.count ("using-key")) {
cerr << "Usage: bmc --generate-key-cert output.cert --using-key human.key" << endl;
return 1;
}
const auto key_path = result ["using-key"].as <string> ();
const auto output_path = result ["generate-key-cert"].as <string> ();
const auto passphrase = get_passphrase_from_user ();
auto key_opt = HumanKeyFile::load (key_path, passphrase);
if (! key_opt) {
cerr << "Error, could not load human key." << endl;
return 1;
}
const auto key = std::move (*key_opt);
cout << "Paste Base64-encoded public key" << endl;
string pubkey_b64;
cin >> pubkey_b64;
auto pubkey_opt = base64_decode (pubkey_b64);
const auto pubkey = std::move (*pubkey_opt);
}
else if (result.count ("test")) {
if (test () != 0) {

View File

@ -2,6 +2,7 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include "json.hpp"
@ -99,28 +100,42 @@ namespace BareMinimumCrypto {
return true;
}
// 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> HumanKeyFile::generate (const string & file_path, const string & passphrase)
{
try_sodium_init ();
string get_passphrase_from_user () {
cout << "Type or paste passphrase (it will be visible in the console)" << endl;
string passphrase;
cin >> passphrase;
if (passphrase.size () < 8) {
return passphrase;
}
optional <json> try_load_msgpack_file (const string & file_path) {
ifstream f;
f.open (file_path, ifstream::binary);
if (! f.is_open ()) {
return nullopt;
}
f.seekg (0, ifstream::end);
const auto len = f.tellg ();
f.seekg (0, ifstream::beg);
if (len > 1024 * 1024) {
return nullopt;
}
Bytes bytes;
bytes.resize (len);
f.read ((char *)bytes.data (), bytes.size ());
return json::from_msgpack (bytes);
}
optional <SigningKey> HumanKeyFile::unlock_key (const Bytes & salt, const string & passphrase)
{
Bytes seed;
seed.resize (crypto_sign_SEEDBYTES);
Bytes salt;
salt.resize (crypto_pwhash_SALTBYTES);
randombytes_buf (salt.data (), salt.size ());
if (crypto_pwhash (
seed.data (), seed.size (),
passphrase.data (), passphrase.size (),
@ -142,6 +157,34 @@ namespace BareMinimumCrypto {
return nullopt;
}
return key;
}
// 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> HumanKeyFile::generate (const string & file_path, const string & passphrase)
{
try_sodium_init ();
if (passphrase.size () < 8) {
return nullopt;
}
Bytes salt;
salt.resize (crypto_pwhash_SALTBYTES);
randombytes_buf (salt.data (), salt.size ());
auto key_opt = unlock_key (salt, passphrase);
if (! key_opt) {
return nullopt;
}
const auto key = std::move (*key_opt);
const auto machine_id = get_machine_id ();
HumanKeyFile key_on_disk {
@ -159,6 +202,16 @@ namespace BareMinimumCrypto {
return key;
}
optional <SigningKey> HumanKeyFile::load (const string & file_path, const string & passphrase)
{
const auto j = std::move (*try_load_msgpack_file (file_path));
const auto human_key = std::move (*HumanKeyFile::try_from_msgpack (j));
const auto key = std::move (*unlock_key (human_key.salt, passphrase));
return key;
}
optional <SigningKey> MachineKeyFile::generate (const string & file_path)
{
const SigningKey key;

View File

@ -15,14 +15,14 @@ namespace BareMinimumCrypto {
using nlohmann::json;
string get_machine_id ();
string get_passphrase_from_user ();
optional <json> try_load_msgpack_file (const string & file_path);
struct SigningKey {
Bytes sk;
SigningKey ();
static optional <SigningKey> load_human_key_file (const string & file_path, const string & passphrase);
Bytes pubkey () const;
Bytes pub_to_msgpack () const;
@ -47,6 +47,10 @@ namespace BareMinimumCrypto {
static optional <SigningKey> generate (const string & file_path, const string & passphrase);
static optional <SigningKey> load (const string & file_path, const string & passphrase);
static optional <SigningKey> unlock_key (const Bytes & salt, const string & passphrase);
Bytes to_msgpack () const;
static optional <HumanKeyFile> try_from_msgpack (const json & msg);
};