♻️ 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 ( 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;

View File

@ -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;
} }

View File

@ -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 ();
} }