update: working on certificates
parent
89213ebf8a
commit
5e18bf8ace
|
@ -1,3 +1,4 @@
|
||||||
/bmc
|
/bmc
|
||||||
|
*.cert
|
||||||
*.key
|
*.key
|
||||||
*.o
|
*.o
|
||||||
|
|
|
@ -28,12 +28,24 @@ int file (const string & file_path) {
|
||||||
|
|
||||||
const auto j = std::move (*try_load_msgpack_file (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"];
|
const string schema = j ["schema"];
|
||||||
|
|
||||||
cout << "Schema: " << schema << endl;
|
cout << "Schema: " << schema << endl;
|
||||||
|
|
||||||
if (schema == "3T6XF5DZ") {
|
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
|
// Read msgpack fields
|
||||||
const auto key = std::move (*HumanKeyFile::try_from_msgpack (j));
|
const auto key = std::move (*HumanKeyFile::try_from_msgpack (j));
|
||||||
|
@ -60,7 +72,7 @@ int file (const string & file_path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (schema == "2PVHIKMA") {
|
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
|
// Read msgpack fields
|
||||||
const auto key = std::move (*MachineKeyFile::try_from_msgpack (j));
|
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;
|
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 {
|
else {
|
||||||
cout << "Unknown schema. Maybe this file is from a newer version of BMC?" << endl;
|
cout << "Unknown schema. Maybe this file is from a newer version of BMC?" << endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -118,7 +169,7 @@ int test () {
|
||||||
cerr << "Sender key " << base64_encode (sender_key.pubkey ()) << endl;
|
cerr << "Sender key " << base64_encode (sender_key.pubkey ()) << endl;
|
||||||
|
|
||||||
// The root signs the sender key
|
// 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));
|
const auto sender = std::move (*Sender::create (sender_key, sender_cert));
|
||||||
|
|
||||||
|
@ -221,8 +272,17 @@ int main (int argc, char ** argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const auto sig = std::move (*sig_opt);
|
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")) {
|
else if (result.count ("test")) {
|
||||||
if (test () != 0) {
|
if (test () != 0) {
|
||||||
|
|
|
@ -13,6 +13,18 @@ namespace BareMinimumCrypto {
|
||||||
return ! (*this == o);
|
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 {
|
Bytes KeyCertFile::to_msgpack () const {
|
||||||
const json cert_j {
|
const json cert_j {
|
||||||
{"pubkey", json::binary (pubkey)},
|
{"pubkey", json::binary (pubkey)},
|
||||||
|
@ -22,12 +34,10 @@ namespace BareMinimumCrypto {
|
||||||
|
|
||||||
const auto cert = json::to_msgpack (cert_j);
|
const auto cert = json::to_msgpack (cert_j);
|
||||||
|
|
||||||
const json j {
|
return ExpiringSignature {
|
||||||
{"sig", json::binary (sig)},
|
cert,
|
||||||
{"cert", json::binary (cert)},
|
sig,
|
||||||
};
|
}.to_msgpack ();
|
||||||
|
|
||||||
return json::to_msgpack (j);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional <KeyCertFile> KeyCertFile::try_from_msgpack (const json & msg)
|
optional <KeyCertFile> KeyCertFile::try_from_msgpack (const json & msg)
|
||||||
|
|
|
@ -18,9 +18,12 @@ namespace BareMinimumCrypto {
|
||||||
// Payload is contained in here
|
// Payload is contained in here
|
||||||
Bytes cert;
|
Bytes cert;
|
||||||
Bytes sig;
|
Bytes sig;
|
||||||
|
Bytes signer_pubkey;
|
||||||
|
|
||||||
bool operator == (const ExpiringSignature & o) const;
|
bool operator == (const ExpiringSignature & o) const;
|
||||||
bool operator != (const ExpiringSignature & o) const;
|
bool operator != (const ExpiringSignature & o) const;
|
||||||
|
|
||||||
|
Bytes to_msgpack () const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyCertFile {
|
struct KeyCertFile {
|
||||||
|
|
|
@ -26,6 +26,8 @@ namespace BareMinimumCrypto {
|
||||||
|
|
||||||
Bytes HumanKeyFile::to_msgpack () const {
|
Bytes HumanKeyFile::to_msgpack () const {
|
||||||
const auto j = json {
|
const auto j = json {
|
||||||
|
// All BMC msgpack artifacts should have this string
|
||||||
|
{"app", "4B27CL32"},
|
||||||
// Breaking changes should generate a new Base32 schema.
|
// Breaking changes should generate a new Base32 schema.
|
||||||
{"schema", "3T6XF5DZ"},
|
{"schema", "3T6XF5DZ"},
|
||||||
{"salt", json::binary (salt)},
|
{"salt", json::binary (salt)},
|
||||||
|
@ -38,6 +40,9 @@ namespace BareMinimumCrypto {
|
||||||
|
|
||||||
optional <HumanKeyFile> HumanKeyFile::try_from_msgpack (const json & j)
|
optional <HumanKeyFile> HumanKeyFile::try_from_msgpack (const json & j)
|
||||||
{
|
{
|
||||||
|
if (j ["app"] != "4B27CL32") {
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
if (j ["schema"] != "3T6XF5DZ") {
|
if (j ["schema"] != "3T6XF5DZ") {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +57,8 @@ namespace BareMinimumCrypto {
|
||||||
|
|
||||||
Bytes MachineKeyFile::to_msgpack () const {
|
Bytes MachineKeyFile::to_msgpack () const {
|
||||||
const auto j = json {
|
const auto j = json {
|
||||||
|
// All BMC msgpack artifacts should have this string
|
||||||
|
{"app", "4B27CL32"},
|
||||||
// Breaking changes should generate a new Base32 schema.
|
// Breaking changes should generate a new Base32 schema.
|
||||||
{"schema", "2PVHIKMA"},
|
{"schema", "2PVHIKMA"},
|
||||||
{"secretkey", json::binary (secretkey)},
|
{"secretkey", json::binary (secretkey)},
|
||||||
|
@ -63,6 +70,9 @@ namespace BareMinimumCrypto {
|
||||||
|
|
||||||
optional <MachineKeyFile> MachineKeyFile::try_from_msgpack (const json & j)
|
optional <MachineKeyFile> MachineKeyFile::try_from_msgpack (const json & j)
|
||||||
{
|
{
|
||||||
|
if (j ["app"] != "4B27CL32") {
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
if (j ["schema"] != "2PVHIKMA") {
|
if (j ["schema"] != "2PVHIKMA") {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
@ -282,12 +292,13 @@ namespace BareMinimumCrypto {
|
||||||
return ExpiringSignature {
|
return ExpiringSignature {
|
||||||
cert,
|
cert,
|
||||||
sig,
|
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
|
optional <ExpiringSignature> SigningKey::sign_data (const Bytes & v, Instant now) const
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace BareMinimumCrypto {
|
||||||
TimeRange tr
|
TimeRange tr
|
||||||
) const;
|
) 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;
|
optional <ExpiringSignature> sign_data (const Bytes & v, Instant now) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
- 2PVHIKMA
|
- 2PVHIKMA
|
||||||
- 3T6XF5DZ
|
- 3T6XF5DZ
|
||||||
|
- 4B27CL32
|
||||||
|
- MSYZGDBI
|
||||||
|
|
||||||
2PVHIKMA is a secret key for machines. It has no passphrase, so
|
2PVHIKMA is a secret key for machines. It has no passphrase, so
|
||||||
computer programs can use it automatically.
|
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.
|
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,
|
The passphrase should be saved in a password manager or written on paper,
|
||||||
and not kept in the same disk as the key.
|
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