ptth/bare_minimum_crypto/cpp/signing_key.cpp

136 lines
3.0 KiB
C++

#include "signing_key.h"
#include <fstream>
#include "json.hpp"
#include "sodium_helpers.h"
namespace BareMinimumCrypto {
using nlohmann::json;
vector <uint8_t> SigningKeyFile::to_msgpack () const {
const auto j = json {
{"salt", json::binary (salt)},
{"time_created", time_created.x},
{"pubkey", json::binary (pubkey)},
};
return json::to_msgpack (j);
}
// 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;
}
SigningKeyFile key_on_disk {
salt,
Instant::now (),
key.pk
};
const auto msg = key_on_disk.to_msgpack ();
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 ();
pk.resize (crypto_sign_PUBLICKEYBYTES);
sk.resize (crypto_sign_SECRETKEYBYTES);
crypto_sign_keypair (pk.data (), sk.data ());
}
vector <uint8_t> SigningKey::pubkey () const {
return pk;
}
vector <uint8_t> SigningKey::pub_to_msgpack () const {
const json j = {
{"key", json::binary (pk)},
};
return json::to_msgpack (j);
}
optional <ExpiringSignature> SigningKey::sign (
const vector <uint8_t> & payload,
TimeRange tr
) const {
try_sodium_init ();
if (tr.duration () > about_1_year) {
return nullopt;
}
const json j {
{"not_before", tr.not_before},
{"not_after", tr.not_after},
{"payload", json::binary (payload)},
};
const auto cert = json::to_msgpack (j);
vector <uint8_t> sig;
sig.resize (crypto_sign_BYTES);
crypto_sign_detached (sig.data (), nullptr, cert.data (), cert.size (), sk.data ());
return ExpiringSignature {
cert,
sig,
};
}
optional <ExpiringSignature> SigningKey::sign_key (const SigningKey & k, Instant now) const
{
return sign (k.pub_to_msgpack (), TimeRange::from_start_and_dur (now, about_3_months));
}
optional <ExpiringSignature> SigningKey::sign_data (const vector <uint8_t> & v, Instant now) const
{
return sign (v, TimeRange::from_start_and_dur (now, about_1_week));
}
}