♻️ refactor: Extract Instant struct
parent
026db25ba7
commit
fb1e133ca1
|
@ -48,7 +48,7 @@ bool is_pubkey_length (const vector <uint8_t> & v) {
|
||||||
optional <vector <uint8_t>> try_verify_signed_data (
|
optional <vector <uint8_t>> try_verify_signed_data (
|
||||||
const ExpiringSignature & sig,
|
const ExpiringSignature & sig,
|
||||||
const vector <uint8_t> & pubkey,
|
const vector <uint8_t> & pubkey,
|
||||||
int64_t now
|
Instant now
|
||||||
) {
|
) {
|
||||||
try_sodium_init ();
|
try_sodium_init ();
|
||||||
|
|
||||||
|
@ -67,13 +67,12 @@ optional <vector <uint8_t>> try_verify_signed_data (
|
||||||
|
|
||||||
const json j = json::parse (sig.cert_s);
|
const json j = json::parse (sig.cert_s);
|
||||||
|
|
||||||
const int64_t not_before = j ["not_before"];
|
const TimeRange tr {
|
||||||
const int64_t not_after = j ["not_after"];
|
j ["not_before"],
|
||||||
|
j ["not_after"]
|
||||||
|
};
|
||||||
|
|
||||||
if (now < not_before) {
|
if (! tr.contains (now)) {
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
if (now > not_after) {
|
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +85,7 @@ optional <vector <uint8_t>> try_verify_signed_data (
|
||||||
optional <vector <uint8_t>> verify_signed_data (
|
optional <vector <uint8_t>> verify_signed_data (
|
||||||
const ExpiringSignature & sig,
|
const ExpiringSignature & sig,
|
||||||
const vector <uint8_t> & pubkey,
|
const vector <uint8_t> & pubkey,
|
||||||
int64_t now
|
Instant now
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return try_verify_signed_data (sig, pubkey, now);
|
return try_verify_signed_data (sig, pubkey, now);
|
||||||
|
@ -100,7 +99,7 @@ optional <vector <uint8_t>> verify_cert_and_data (
|
||||||
const ExpiringSignature & signed_cert,
|
const ExpiringSignature & signed_cert,
|
||||||
const ExpiringSignature & signed_data,
|
const ExpiringSignature & signed_data,
|
||||||
const vector <uint8_t> & root_pubkey,
|
const vector <uint8_t> & root_pubkey,
|
||||||
int64_t now
|
Instant now
|
||||||
) {
|
) {
|
||||||
auto subkey_opt = verify_signed_data (signed_cert, root_pubkey, now);
|
auto subkey_opt = verify_signed_data (signed_cert, root_pubkey, now);
|
||||||
if (! subkey_opt) {
|
if (! subkey_opt) {
|
||||||
|
@ -116,14 +115,14 @@ optional <vector <uint8_t>> verify_cert_and_data (
|
||||||
const ExpiringSignature & signed_data,
|
const ExpiringSignature & signed_data,
|
||||||
const vector <uint8_t> & root_pubkey
|
const vector <uint8_t> & root_pubkey
|
||||||
) {
|
) {
|
||||||
return verify_cert_and_data (signed_cert, signed_data, root_pubkey, get_seconds_since_epoch ());
|
return verify_cert_and_data (signed_cert, signed_data, root_pubkey, Instant::now ());
|
||||||
}
|
}
|
||||||
|
|
||||||
optional <vector <uint8_t>> verify_signed_data (
|
optional <vector <uint8_t>> verify_signed_data (
|
||||||
const ExpiringSignature & sig,
|
const ExpiringSignature & sig,
|
||||||
const vector <uint8_t> & pubkey
|
const vector <uint8_t> & pubkey
|
||||||
) {
|
) {
|
||||||
return verify_signed_data (sig, pubkey, get_seconds_since_epoch ());
|
return verify_signed_data (sig, pubkey, Instant::now ());
|
||||||
}
|
}
|
||||||
|
|
||||||
class SigningKey {
|
class SigningKey {
|
||||||
|
@ -150,20 +149,17 @@ public:
|
||||||
|
|
||||||
optional <ExpiringSignature> sign_base64 (
|
optional <ExpiringSignature> sign_base64 (
|
||||||
const string & payload_b64,
|
const string & payload_b64,
|
||||||
int64_t duration,
|
TimeRange tr
|
||||||
int64_t now
|
|
||||||
) const {
|
) const {
|
||||||
try_sodium_init ();
|
try_sodium_init ();
|
||||||
|
|
||||||
if (duration > about_1_year) {
|
if (tr.duration () > about_1_year) {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto not_after = now + duration;
|
|
||||||
|
|
||||||
const json j {
|
const json j {
|
||||||
{"not_before", now},
|
{"not_before", tr.not_before},
|
||||||
{"not_after", not_after},
|
{"not_after", tr.not_after},
|
||||||
{"payload_b64", payload_b64},
|
{"payload_b64", payload_b64},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,14 +176,14 @@ public:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
optional <ExpiringSignature> sign_key (const SigningKey & k, int64_t now) const
|
optional <ExpiringSignature> sign_key (const SigningKey & k, Instant now) const
|
||||||
{
|
{
|
||||||
return sign_base64 (k.pub_to_base64 (), about_3_months, now);
|
return sign_base64 (k.pub_to_base64 (), TimeRange::from_start_and_dur (now, about_3_months));
|
||||||
}
|
}
|
||||||
|
|
||||||
optional <ExpiringSignature> sign_data (const vector <uint8_t> & v, int64_t now) const
|
optional <ExpiringSignature> sign_data (const vector <uint8_t> & v, Instant now) const
|
||||||
{
|
{
|
||||||
return sign_base64 (base64_encode (v), about_1_week, now);
|
return sign_base64 (base64_encode (v), TimeRange::from_start_and_dur (now, about_1_week));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -207,7 +203,7 @@ int happy_path () {
|
||||||
SigningKey signing_key;
|
SigningKey signing_key;
|
||||||
cerr << "Signing key " << signing_key.pub_to_base64 () << endl;
|
cerr << "Signing key " << signing_key.pub_to_base64 () << endl;
|
||||||
|
|
||||||
const auto now = get_seconds_since_epoch ();
|
const auto now = Instant::now ();
|
||||||
|
|
||||||
// That signing key signs some important data
|
// That signing key signs some important data
|
||||||
const auto important_data = copy_to_bytes ("Nikolai, Anna, Ivan, Mikhail, Ivan, Nikolai, Anna. 7 4 1 4 3 5 7 4");
|
const auto important_data = copy_to_bytes ("Nikolai, Anna, Ivan, Mikhail, Ivan, Nikolai, Anna. 7 4 1 4 3 5 7 4");
|
||||||
|
@ -219,7 +215,7 @@ int happy_path () {
|
||||||
{
|
{
|
||||||
// Check that a different time results in a different cert
|
// Check that a different time results in a different cert
|
||||||
const auto cert_2 = std::move (*root_key.sign_key (signing_key, now));
|
const auto cert_2 = std::move (*root_key.sign_key (signing_key, now));
|
||||||
const auto cert_3 = std::move (*root_key.sign_key (signing_key, now + 1));
|
const auto cert_3 = std::move (*root_key.sign_key (signing_key, Instant {now.x + 1}));
|
||||||
|
|
||||||
if (cert != cert_2) {
|
if (cert != cert_2) {
|
||||||
cerr << "Certs should have been identical" << endl;
|
cerr << "Certs should have been identical" << endl;
|
||||||
|
|
|
@ -7,26 +7,66 @@ namespace BareMinimumCrypto {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace chrono;
|
using namespace chrono;
|
||||||
|
|
||||||
int64_t get_seconds_since_epoch () {
|
Instant::Instant (int64_t x): x (x) {}
|
||||||
|
|
||||||
|
Instant Instant::now () {
|
||||||
const auto utc_now = system_clock::now ();
|
const auto utc_now = system_clock::now ();
|
||||||
return duration_cast <seconds> (utc_now.time_since_epoch ()).count ();
|
return Instant {duration_cast <seconds> (utc_now.time_since_epoch ()).count ()};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeRange::contains (Instant x) const {
|
||||||
|
if (not_after < not_before) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.x < not_before) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.x > not_after) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t TimeRange::duration () const {
|
||||||
|
return not_after - not_before;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeRange TimeRange::from_start_and_dur (Instant start, int64_t dur) {
|
||||||
|
return TimeRange {
|
||||||
|
start.x,
|
||||||
|
start.x + dur
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most tests will use a virtual clock. But just as a smoke test,
|
// Most tests will use a virtual clock. But just as a smoke test,
|
||||||
// make sure real time is realistic.
|
// make sure real time is realistic.
|
||||||
|
|
||||||
int test_time () {
|
int test_time () {
|
||||||
const auto seconds_since_epoch = get_seconds_since_epoch ();
|
const auto now = Instant::now ();
|
||||||
|
|
||||||
const auto time_of_writing = 1610844872;
|
const auto time_of_writing = 1610844872;
|
||||||
if (seconds_since_epoch < time_of_writing) {
|
const int64_t about_100_years = (int64_t)100 * 365 * 86400;
|
||||||
cerr << "Error: Real time is in the past." << endl;
|
|
||||||
|
const TimeRange tr {
|
||||||
|
time_of_writing,
|
||||||
|
time_of_writing + about_100_years
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! tr.contains (now)) {
|
||||||
|
cerr << "System clock is acting weird" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int64_t about_100_years = (int64_t)100 * 365 * 86400;
|
const TimeRange bad_tr {
|
||||||
if (seconds_since_epoch > time_of_writing + about_100_years) {
|
time_of_writing + about_100_years,
|
||||||
cerr << "Error: Real time is in the far future." << endl;
|
time_of_writing
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bad_tr.contains (now)) {
|
||||||
|
cerr << "Invalid TimeRange should not contain any Instants" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,23 @@ namespace BareMinimumCrypto {
|
||||||
const int64_t about_3_months = (int64_t)105 * 86400;
|
const int64_t about_3_months = (int64_t)105 * 86400;
|
||||||
const int64_t about_1_year = (int64_t)365 * 86400;
|
const int64_t about_1_year = (int64_t)365 * 86400;
|
||||||
|
|
||||||
int64_t get_seconds_since_epoch ();
|
struct Instant {
|
||||||
|
// Seconds since Unix epoch
|
||||||
|
int64_t x;
|
||||||
|
|
||||||
|
Instant (int64_t x);
|
||||||
|
static Instant now ();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimeRange {
|
||||||
|
int64_t not_before;
|
||||||
|
int64_t not_after;
|
||||||
|
|
||||||
|
bool contains (Instant x) const;
|
||||||
|
int64_t duration () const;
|
||||||
|
|
||||||
|
static TimeRange from_start_and_dur (Instant start, int64_t dur);
|
||||||
|
};
|
||||||
|
|
||||||
int test_time ();
|
int test_time ();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue