🚧 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) {
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue