update: working on certificates

main
_ 2021-01-21 23:00:10 +00:00
parent 89213ebf8a
commit 5e18bf8ace
7 changed files with 107 additions and 12 deletions

View File

@ -1,3 +1,4 @@
/bmc
*.cert
*.key
*.o

View File

@ -28,12 +28,24 @@ int file (const string & file_path) {
const auto j = std::move (*try_load_msgpack_file (file_path));
if (! j.contains ("app")) {
cout << "Can't find app magic string. This file was probably not made by BMC." << endl;
return 1;
}
const string app = j ["app"];
if (app != "4B27CL32") {
cout << "Can't find app magic string. This file was probably not made by BMC." << endl;
return 1;
}
const string schema = j ["schema"];
cout << "Schema: " << schema << endl;
if (schema == "3T6XF5DZ") {
cout << "File is a secret key for humans (It's passphrase-protected)" << endl;
cout << "File is a secret key for humans. (It's passphrase-protected)" << endl;
// Read msgpack fields
const auto key = std::move (*HumanKeyFile::try_from_msgpack (j));
@ -60,7 +72,7 @@ int file (const string & file_path) {
}
}
else if (schema == "2PVHIKMA") {
cout << "File is a secret key for machines (No passphrase)" << endl;
cout << "File is a secret key for machines. (No passphrase)" << endl;
// Read msgpack fields
const auto key = std::move (*MachineKeyFile::try_from_msgpack (j));
@ -86,8 +98,47 @@ int file (const string & file_path) {
cout << "* The key doesn't have the right permissions. Try `chmod 400` on it." << endl;
}
}
else if (schema == "MSYZGDBI") {
cout << "File is a signed certificate, possibly for a pubkey." << endl;
cout << "Claims to be signed by pubkey `" << base64_encode (j ["signer_pubkey"].get_binary ()) << "`" << endl;
const auto cert_bytes = j ["cert"].get_binary ();
const auto cert = json::from_msgpack (cert_bytes);
const TimeRange tr {
cert ["not_before"],
cert ["not_after"],
};
const auto now = Instant::now ();
if (! tr.contains (now)) {
cout << "Cert is NOT valid." << endl;
if (now.x <= tr.not_before) {
cout << "Cert is from the future. Is your clock behind? Is the signer's clock ahead?" << endl;
}
else if (now.x >= tr.not_after) {
cout << "Cert expired " << now.x - tr.not_after << " seconds ago. Don't trust it." << endl;
}
return 1;
}
cout << "Cert is valid for another " << tr.not_after - now.x << " seconds." << endl;
const auto payload = cert ["payload"].get_binary ();
if (payload.size () == crypto_sign_PUBLICKEYBYTES) {
cout << "Payload might be the pubkey `" << base64_encode (payload) << "`" << endl;
}
else {
cout << "Payload doesn't have the right size to be a pubkey." << endl;
}
}
else {
cout << "Unknown schema. Maybe this file is from a newer version of BMC?" << endl;
return 1;
}
return 0;
@ -118,7 +169,7 @@ int test () {
cerr << "Sender key " << base64_encode (sender_key.pubkey ()) << endl;
// The root signs the sender key
const ExpiringSignature sender_cert = std::move (*root_key.sign_key (sender_key, now));
const auto sender_cert = std::move (*root_key.sign_key (sender_key.pubkey (), now));
const auto sender = std::move (*Sender::create (sender_key, sender_cert));
@ -221,8 +272,17 @@ int main (int argc, char ** argv) {
return 1;
}
const auto sig = std::move (*sig_opt);
const auto sig_bytes = sig.to_msgpack ();
ofstream f;
f.open (output_path, ofstream::binary);
if (! f.is_open ()) {
cerr << "Error, could not open output file" << endl;
return 1;
}
f.write ((const char *)sig_bytes.data (), sig_bytes.size ());
}
else if (result.count ("test")) {
if (test () != 0) {

View File

@ -13,6 +13,18 @@ namespace BareMinimumCrypto {
return ! (*this == o);
}
Bytes ExpiringSignature::to_msgpack () const {
const json j {
{"app", "4B27CL32"},
{"schema", "MSYZGDBI"},
{"sig", json::binary (sig)},
{"cert", json::binary (cert)},
{"signer_pubkey", json::binary (signer_pubkey)},
};
return json::to_msgpack (j);
}
Bytes KeyCertFile::to_msgpack () const {
const json cert_j {
{"pubkey", json::binary (pubkey)},
@ -22,12 +34,10 @@ namespace BareMinimumCrypto {
const auto cert = json::to_msgpack (cert_j);
const json j {
{"sig", json::binary (sig)},
{"cert", json::binary (cert)},
};
return json::to_msgpack (j);
return ExpiringSignature {
cert,
sig,
}.to_msgpack ();
}
optional <KeyCertFile> KeyCertFile::try_from_msgpack (const json & msg)

View File

@ -18,9 +18,12 @@ namespace BareMinimumCrypto {
// Payload is contained in here
Bytes cert;
Bytes sig;
Bytes signer_pubkey;
bool operator == (const ExpiringSignature & o) const;
bool operator != (const ExpiringSignature & o) const;
Bytes to_msgpack () const;
};
struct KeyCertFile {

View File

@ -26,6 +26,8 @@ namespace BareMinimumCrypto {
Bytes HumanKeyFile::to_msgpack () const {
const auto j = json {
// All BMC msgpack artifacts should have this string
{"app", "4B27CL32"},
// Breaking changes should generate a new Base32 schema.
{"schema", "3T6XF5DZ"},
{"salt", json::binary (salt)},
@ -38,6 +40,9 @@ namespace BareMinimumCrypto {
optional <HumanKeyFile> HumanKeyFile::try_from_msgpack (const json & j)
{
if (j ["app"] != "4B27CL32") {
return nullopt;
}
if (j ["schema"] != "3T6XF5DZ") {
return nullopt;
}
@ -52,6 +57,8 @@ namespace BareMinimumCrypto {
Bytes MachineKeyFile::to_msgpack () const {
const auto j = json {
// All BMC msgpack artifacts should have this string
{"app", "4B27CL32"},
// Breaking changes should generate a new Base32 schema.
{"schema", "2PVHIKMA"},
{"secretkey", json::binary (secretkey)},
@ -63,6 +70,9 @@ namespace BareMinimumCrypto {
optional <MachineKeyFile> MachineKeyFile::try_from_msgpack (const json & j)
{
if (j ["app"] != "4B27CL32") {
return nullopt;
}
if (j ["schema"] != "2PVHIKMA") {
return nullopt;
}
@ -282,12 +292,13 @@ namespace BareMinimumCrypto {
return ExpiringSignature {
cert,
sig,
pubkey (),
};
}
optional <ExpiringSignature> SigningKey::sign_key (const SigningKey & k, Instant now) const
optional <ExpiringSignature> SigningKey::sign_key (const Bytes & pubkey, Instant now) const
{
return sign (k.pub_to_msgpack (), TimeRange::from_start_and_dur (now, about_3_months));
return sign (pubkey, TimeRange::from_start_and_dur (now, about_3_months));
}
optional <ExpiringSignature> SigningKey::sign_data (const Bytes & v, Instant now) const

View File

@ -31,7 +31,7 @@ namespace BareMinimumCrypto {
TimeRange tr
) const;
optional <ExpiringSignature> sign_key (const SigningKey & k, Instant now) const;
optional <ExpiringSignature> sign_key (const Bytes & pubkey, Instant now) const;
optional <ExpiringSignature> sign_data (const Bytes & v, Instant now) const;
};

View File

@ -1,5 +1,7 @@
- 2PVHIKMA
- 3T6XF5DZ
- 4B27CL32
- MSYZGDBI
2PVHIKMA is a secret key for machines. It has no passphrase, so
computer programs can use it automatically.
@ -7,3 +9,11 @@ computer programs can use it automatically.
3T6XF5DZ is a secret key for humans. It is protected by a passphrase.
The passphrase should be saved in a password manager or written on paper,
and not kept in the same disk as the key.
4B27CL32 can be used by older versions of BMC to differentiate between
generic Msgpack files, and files created by newer versions of BMC. This is
in addition to the schema, since older versions won't recognize newer
schemas.
MSYZGDBI is a signed certificate. The payload is in the file and may be a
public key.