🚧 wip: working on process for a human key signing other keys
parent
02346c3285
commit
ddba8953fa
|
@ -25,25 +25,8 @@ namespace fs = std::filesystem;
|
||||||
|
|
||||||
int file (const string & file_path) {
|
int file (const string & file_path) {
|
||||||
cout << "Reading `" << file_path << "`" << endl;
|
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 j = std::move (*try_load_msgpack_file (file_path));
|
||||||
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 string schema = j ["schema"];
|
const string schema = j ["schema"];
|
||||||
|
|
||||||
|
@ -164,13 +147,18 @@ int test () {
|
||||||
int main (int argc, char ** argv) {
|
int main (int argc, char ** argv) {
|
||||||
cxxopts::Options options ("BareMinimumCrypto", "Simple crypto things you might need.");
|
cxxopts::Options options ("BareMinimumCrypto", "Simple crypto things you might need.");
|
||||||
options.add_options ()
|
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-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-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> ())
|
("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")
|
("test", "Run self-test")
|
||||||
("help", "Print usage")
|
|
||||||
;
|
;
|
||||||
|
|
||||||
auto result = options.parse (argc, argv);
|
auto result = options.parse (argc, argv);
|
||||||
|
@ -178,11 +166,13 @@ int main (int argc, char ** argv) {
|
||||||
if (result.count ("help")) {
|
if (result.count ("help")) {
|
||||||
cout << options.help () << endl;
|
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")) {
|
else if (result.count ("generate-human-key")) {
|
||||||
const auto file_path = result ["generate-human-key"].as <string> ();
|
const auto file_path = result ["generate-human-key"].as <string> ();
|
||||||
cout << "Type passphrase (it will be visible in the console)" << endl;
|
const auto passphrase = get_passphrase_from_user ();
|
||||||
string passphrase;
|
|
||||||
cin >> passphrase;
|
|
||||||
|
|
||||||
auto key_opt = HumanKeyFile::generate (file_path, passphrase);
|
auto key_opt = HumanKeyFile::generate (file_path, passphrase);
|
||||||
if (! key_opt) {
|
if (! key_opt) {
|
||||||
|
@ -201,9 +191,31 @@ int main (int argc, char ** argv) {
|
||||||
}
|
}
|
||||||
cout << "The pubkey is `" << base64_encode (key_opt->pubkey ()) << "`" << endl;
|
cout << "The pubkey is `" << base64_encode (key_opt->pubkey ()) << "`" << endl;
|
||||||
}
|
}
|
||||||
else if (result.count ("file")) {
|
else if (result.count ("generate-key-cert")) {
|
||||||
const auto file_path = result ["file"].as <string> ();
|
if (! result.count ("using-key")) {
|
||||||
return file (file_path);
|
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")) {
|
else if (result.count ("test")) {
|
||||||
if (test () != 0) {
|
if (test () != 0) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
|
@ -99,28 +100,42 @@ namespace BareMinimumCrypto {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The whole process for a passphrased key is like this:
|
string get_passphrase_from_user () {
|
||||||
// Passphrase + random salt -->
|
cout << "Type or paste passphrase (it will be visible in the console)" << endl;
|
||||||
// Seed -->
|
string passphrase;
|
||||||
// Secret key + Public key
|
cin >> passphrase;
|
||||||
|
|
||||||
// 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 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;
|
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;
|
Bytes seed;
|
||||||
seed.resize (crypto_sign_SEEDBYTES);
|
seed.resize (crypto_sign_SEEDBYTES);
|
||||||
|
|
||||||
Bytes salt;
|
|
||||||
salt.resize (crypto_pwhash_SALTBYTES);
|
|
||||||
randombytes_buf (salt.data (), salt.size ());
|
|
||||||
|
|
||||||
if (crypto_pwhash (
|
if (crypto_pwhash (
|
||||||
seed.data (), seed.size (),
|
seed.data (), seed.size (),
|
||||||
passphrase.data (), passphrase.size (),
|
passphrase.data (), passphrase.size (),
|
||||||
|
@ -142,6 +157,34 @@ namespace BareMinimumCrypto {
|
||||||
return nullopt;
|
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 ();
|
const auto machine_id = get_machine_id ();
|
||||||
|
|
||||||
HumanKeyFile key_on_disk {
|
HumanKeyFile key_on_disk {
|
||||||
|
@ -159,6 +202,16 @@ namespace BareMinimumCrypto {
|
||||||
return key;
|
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)
|
optional <SigningKey> MachineKeyFile::generate (const string & file_path)
|
||||||
{
|
{
|
||||||
const SigningKey key;
|
const SigningKey key;
|
||||||
|
|
|
@ -15,14 +15,14 @@ namespace BareMinimumCrypto {
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
string get_machine_id ();
|
string get_machine_id ();
|
||||||
|
string get_passphrase_from_user ();
|
||||||
|
optional <json> try_load_msgpack_file (const string & file_path);
|
||||||
|
|
||||||
struct SigningKey {
|
struct SigningKey {
|
||||||
Bytes sk;
|
Bytes sk;
|
||||||
|
|
||||||
SigningKey ();
|
SigningKey ();
|
||||||
|
|
||||||
static optional <SigningKey> load_human_key_file (const string & file_path, const string & passphrase);
|
|
||||||
|
|
||||||
Bytes pubkey () const;
|
Bytes pubkey () const;
|
||||||
Bytes pub_to_msgpack () 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> 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;
|
Bytes to_msgpack () const;
|
||||||
static optional <HumanKeyFile> try_from_msgpack (const json & msg);
|
static optional <HumanKeyFile> try_from_msgpack (const json & msg);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue