update: working on certificates
parent
89213ebf8a
commit
5e18bf8ace
|
@ -1,3 +1,4 @@
|
|||
/bmc
|
||||
*.cert
|
||||
*.key
|
||||
*.o
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue