♻️ refactor: Extract Instant struct

main
_ 2021-01-17 17:31:28 -06:00
parent 026db25ba7
commit fb1e133ca1
3 changed files with 85 additions and 33 deletions

View File

@ -48,7 +48,7 @@ bool is_pubkey_length (const vector <uint8_t> & v) {
optional <vector <uint8_t>> try_verify_signed_data (
const ExpiringSignature & sig,
const vector <uint8_t> & pubkey,
int64_t now
Instant now
) {
try_sodium_init ();
@ -67,13 +67,12 @@ optional <vector <uint8_t>> try_verify_signed_data (
const json j = json::parse (sig.cert_s);
const int64_t not_before = j ["not_before"];
const int64_t not_after = j ["not_after"];
const TimeRange tr {
j ["not_before"],
j ["not_after"]
};
if (now < not_before) {
return nullopt;
}
if (now > not_after) {
if (! tr.contains (now)) {
return nullopt;
}
@ -86,7 +85,7 @@ optional <vector <uint8_t>> try_verify_signed_data (
optional <vector <uint8_t>> verify_signed_data (
const ExpiringSignature & sig,
const vector <uint8_t> & pubkey,
int64_t now
Instant now
) {
try {
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_data,
const vector <uint8_t> & root_pubkey,
int64_t now
Instant now
) {
auto subkey_opt = verify_signed_data (signed_cert, root_pubkey, now);
if (! subkey_opt) {
@ -116,14 +115,14 @@ optional <vector <uint8_t>> verify_cert_and_data (
const ExpiringSignature & signed_data,
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 (
const ExpiringSignature & sig,
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 {
@ -150,20 +149,17 @@ public:
optional <ExpiringSignature> sign_base64 (
const string & payload_b64,
int64_t duration,
int64_t now
TimeRange tr
) const {
try_sodium_init ();
if (duration > about_1_year) {
if (tr.duration () > about_1_year) {
return nullopt;
}
const auto not_after = now + duration;
const json j {
{"not_before", now},
{"not_after", not_after},
{"not_before", tr.not_before},
{"not_after", tr.not_after},
{"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;
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
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
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) {
cerr << "Certs should have been identical" << endl;

View File

@ -7,26 +7,66 @@ namespace BareMinimumCrypto {
using namespace std;
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 ();
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,
// make sure real time is realistic.
int test_time () {
const auto seconds_since_epoch = get_seconds_since_epoch ();
const auto now = Instant::now ();
const auto time_of_writing = 1610844872;
if (seconds_since_epoch < time_of_writing) {
cerr << "Error: Real time is in the past." << endl;
const int64_t about_100_years = (int64_t)100 * 365 * 86400;
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;
}
const int64_t about_100_years = (int64_t)100 * 365 * 86400;
if (seconds_since_epoch > time_of_writing + about_100_years) {
cerr << "Error: Real time is in the far future." << endl;
const TimeRange bad_tr {
time_of_writing + about_100_years,
time_of_writing
};
if (bad_tr.contains (now)) {
cerr << "Invalid TimeRange should not contain any Instants" << endl;
return 1;
}

View File

@ -7,7 +7,23 @@ namespace BareMinimumCrypto {
const int64_t about_3_months = (int64_t)105 * 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 ();
}