LibCrypto: Store ASN1 certificate timestamps as UnixDateTime

We are currently using Core::DateTime, which is meant to represent local
time. However, we are doing no conversion between the parsed time in UTC
and local time, so we end up comparing time stamps from different time
zones.

Instead, store the parsed times as UnixDateTime, which is UTC. Then we
can always compare the parsed times against the current UTC time.

This also lets us store parsed milliseconds.
This commit is contained in:
Timothy Flynn 2024-03-07 10:34:45 -05:00 committed by Ali Mohammad Pur
parent da118f2adf
commit 928287b782
Notes: sideshowbarker 2024-07-17 06:51:48 +09:00
7 changed files with 64 additions and 65 deletions

View file

@ -5,11 +5,12 @@
*/
#include <AK/StringView.h>
#include <AK/Time.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibTest/TestCase.h>
#define EXPECT_DATETIME(sv, y, mo, d, h, mi, s) \
EXPECT_EQ(Crypto::ASN1::parse_utc_time(sv).value(), Core::DateTime::create(y, mo, d, h, mi, s))
EXPECT_EQ(Crypto::ASN1::parse_utc_time(sv).value(), UnixDateTime::from_unix_time_parts(y, mo, d, h, mi, s, 0))
TEST_CASE(test_utc_boring)
{
@ -65,79 +66,76 @@ TEST_CASE(test_utc_missing_z)
}
#undef EXPECT_DATETIME
#define EXPECT_DATETIME(sv, y, mo, d, h, mi, s) \
EXPECT_EQ(Crypto::ASN1::parse_generalized_time(sv).value(), Core::DateTime::create(y, mo, d, h, mi, s))
#define EXPECT_DATETIME(sv, y, mo, d, h, mi, s, ms) \
EXPECT_EQ(Crypto::ASN1::parse_generalized_time(sv).value(), UnixDateTime::from_unix_time_parts(y, mo, d, h, mi, s, ms))
TEST_CASE(test_generalized_boring)
{
// YYYYMMDDhh[mm[ss[.fff]]]
EXPECT_DATETIME("20010101010101Z"sv, 2001, 1, 1, 1, 1, 1);
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20020406081012Z"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("200204060810Z"sv, 2002, 4, 6, 8, 10, 0);
EXPECT_DATETIME("2002040608Z"sv, 2002, 4, 6, 8, 0, 0);
// TODO: We probably should not discard the milliseconds.
EXPECT_DATETIME("20020406081012.567Z"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("20220911220000Z"sv, 2022, 9, 11, 22, 0, 0);
EXPECT_DATETIME("20010101010101Z"sv, 2001, 1, 1, 1, 1, 1, 0);
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("20020406081012Z"sv, 2002, 4, 6, 8, 10, 12, 0);
EXPECT_DATETIME("200204060810Z"sv, 2002, 4, 6, 8, 10, 0, 0);
EXPECT_DATETIME("2002040608Z"sv, 2002, 4, 6, 8, 0, 0, 0);
EXPECT_DATETIME("20020406081012.567Z"sv, 2002, 4, 6, 8, 10, 12, 567);
EXPECT_DATETIME("20220911220000Z"sv, 2022, 9, 11, 22, 0, 0, 0);
}
TEST_CASE(test_generalized_offset)
{
// YYYYMMDDhh[mm[ss[.fff]]](+|-)hhmm
// We don't yet support storing the offset anywhere and instead just assume that the offset is just +0000.
EXPECT_DATETIME("20010101010101+0000"sv, 2001, 1, 1, 1, 1, 1);
EXPECT_DATETIME("20010203040506+0000"sv, 2001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20020406081012+0000"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("200204060810+0000"sv, 2002, 4, 6, 8, 10, 0);
EXPECT_DATETIME("2002040608+0000"sv, 2002, 4, 6, 8, 0, 0);
// TODO: We probably should not discard the milliseconds.
EXPECT_DATETIME("20020406081012.567+0000"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("20220911220000+0000"sv, 2022, 9, 11, 22, 0, 0);
EXPECT_DATETIME("20010101010101+0000"sv, 2001, 1, 1, 1, 1, 1, 0);
EXPECT_DATETIME("20010203040506+0000"sv, 2001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("20020406081012+0000"sv, 2002, 4, 6, 8, 10, 12, 0);
EXPECT_DATETIME("200204060810+0000"sv, 2002, 4, 6, 8, 10, 0, 0);
EXPECT_DATETIME("2002040608+0000"sv, 2002, 4, 6, 8, 0, 0, 0);
EXPECT_DATETIME("20020406081012.567+0000"sv, 2002, 4, 6, 8, 10, 12, 567);
EXPECT_DATETIME("20220911220000+0000"sv, 2022, 9, 11, 22, 0, 0, 0);
// Designed to fail once we support offsets:
EXPECT_DATETIME("20220911220000+0600"sv, 2022, 9, 11, 22, 0, 0);
EXPECT_DATETIME("20220911220000+0600"sv, 2022, 9, 11, 22, 0, 0, 0);
}
TEST_CASE(test_generalized_missing_z)
{
// YYYYMMDDhh[mm[ss[.fff]]]
EXPECT_DATETIME("20010101010101"sv, 2001, 1, 1, 1, 1, 1);
EXPECT_DATETIME("20010203040506"sv, 2001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20020406081012"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("200204060810"sv, 2002, 4, 6, 8, 10, 0);
EXPECT_DATETIME("2002040608"sv, 2002, 4, 6, 8, 0, 0);
// TODO: We probably should not discard the milliseconds.
EXPECT_DATETIME("20020406081012.567"sv, 2002, 4, 6, 8, 10, 12);
EXPECT_DATETIME("20220911220000"sv, 2022, 9, 11, 22, 0, 0);
EXPECT_DATETIME("20010101010101"sv, 2001, 1, 1, 1, 1, 1, 0);
EXPECT_DATETIME("20010203040506"sv, 2001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("20020406081012"sv, 2002, 4, 6, 8, 10, 12, 0);
EXPECT_DATETIME("200204060810"sv, 2002, 4, 6, 8, 10, 0, 0);
EXPECT_DATETIME("2002040608"sv, 2002, 4, 6, 8, 0, 0, 0);
EXPECT_DATETIME("20020406081012.567"sv, 2002, 4, 6, 8, 10, 12, 567);
EXPECT_DATETIME("20220911220000"sv, 2022, 9, 11, 22, 0, 0, 0);
}
TEST_CASE(test_generalized_unusual_year)
{
// Towards the positive
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20110203040506Z"sv, 2011, 2, 3, 4, 5, 6);
EXPECT_DATETIME("21010203040506Z"sv, 2101, 2, 3, 4, 5, 6);
EXPECT_DATETIME("30010203040506Z"sv, 3001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("40010203040506Z"sv, 4001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("90010203040506Z"sv, 9001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("99990203040506Z"sv, 9999, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("20110203040506Z"sv, 2011, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("21010203040506Z"sv, 2101, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("30010203040506Z"sv, 3001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("40010203040506Z"sv, 4001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("90010203040506Z"sv, 9001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("99990203040506Z"sv, 9999, 2, 3, 4, 5, 6, 0);
// Towards zero
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("19990203040506Z"sv, 1999, 2, 3, 4, 5, 6);
EXPECT_DATETIME("19500203040506Z"sv, 1950, 2, 3, 4, 5, 6);
EXPECT_DATETIME("19010203040506Z"sv, 1901, 2, 3, 4, 5, 6);
EXPECT_DATETIME("18010203040506Z"sv, 1801, 2, 3, 4, 5, 6);
EXPECT_DATETIME("15010203040506Z"sv, 1501, 2, 3, 4, 5, 6);
EXPECT_DATETIME("10010203040506Z"sv, 1001, 2, 3, 4, 5, 6);
EXPECT_DATETIME("01010203040506Z"sv, 101, 2, 3, 4, 5, 6);
EXPECT_DATETIME("00110203040506Z"sv, 11, 2, 3, 4, 5, 6);
EXPECT_DATETIME("00010203040506Z"sv, 1, 2, 3, 4, 5, 6);
EXPECT_DATETIME("00000203040506Z"sv, 0, 2, 3, 4, 5, 6);
EXPECT_DATETIME("20010203040506Z"sv, 2001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("19990203040506Z"sv, 1999, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("19500203040506Z"sv, 1950, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("19010203040506Z"sv, 1901, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("18010203040506Z"sv, 1801, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("15010203040506Z"sv, 1501, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("10010203040506Z"sv, 1001, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("01010203040506Z"sv, 101, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("00110203040506Z"sv, 11, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("00010203040506Z"sv, 1, 2, 3, 4, 5, 6, 0);
EXPECT_DATETIME("00000203040506Z"sv, 0, 2, 3, 4, 5, 6, 0);
// Problematic dates
EXPECT_DATETIME("20200229040506Z"sv, 2020, 2, 29, 4, 5, 6);
EXPECT_DATETIME("20000229040506Z"sv, 2000, 2, 29, 4, 5, 6);
EXPECT_DATETIME("24000229040506Z"sv, 2400, 2, 29, 4, 5, 6);
EXPECT_DATETIME("20200229040506Z"sv, 2020, 2, 29, 4, 5, 6, 0);
EXPECT_DATETIME("20000229040506Z"sv, 2000, 2, 29, 4, 5, 6, 0);
EXPECT_DATETIME("24000229040506Z"sv, 2400, 2, 29, 4, 5, 6, 0);
}
TEST_CASE(test_generalized_nonexistent_dates)

View file

@ -6,6 +6,7 @@
#include "CertificateStoreWidget.h"
#include <AK/String.h>
#include <LibCore/DateTime.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibFileSystem/FileSystem.h>
#include <LibFileSystemAccessClient/Client.h>
@ -74,7 +75,7 @@ GUI::Variant CertificateStoreModel::data(GUI::ModelIndex const& index, GUI::Mode
return issued_by;
}
case Column::Expire:
return cert.validity.not_after.to_byte_string("%Y-%m-%d"sv);
return Core::DateTime::from_timestamp(cert.validity.not_after.seconds_since_epoch()).to_byte_string("%Y-%m-%d"sv);
default:
VERIFY_NOT_REACHED();
}

View file

@ -119,7 +119,7 @@ ByteString type_name(Type type)
return "InvalidType";
}
Optional<Core::DateTime> parse_utc_time(StringView time)
Optional<UnixDateTime> parse_utc_time(StringView time)
{
// YYMMDDhhmm[ss]Z or YYMMDDhhmm[ss](+|-)hhmm
GenericLexer lexer(time);
@ -164,10 +164,10 @@ Optional<Core::DateTime> parse_utc_time(StringView time)
if (offset_hours.has_value() || offset_minutes.has_value())
dbgln("FIXME: Implement UTCTime with offset!");
return Core::DateTime::create(full_year, month.value(), day.value(), hour.value(), minute.value(), full_seconds);
return UnixDateTime::from_unix_time_parts(full_year, month.value(), day.value(), hour.value(), minute.value(), full_seconds, 0);
}
Optional<Core::DateTime> parse_generalized_time(StringView time)
Optional<UnixDateTime> parse_generalized_time(StringView time)
{
// YYYYMMDDhh[mm[ss[.fff]]] or YYYYMMDDhh[mm[ss[.fff]]]Z or YYYYMMDDhh[mm[ss[.fff]]](+|-)hhmm
GenericLexer lexer(time);
@ -177,6 +177,7 @@ Optional<Core::DateTime> parse_generalized_time(StringView time)
auto hour = lexer.consume(2).to_number<unsigned>();
Optional<unsigned> minute, seconds, milliseconds, offset_hours, offset_minutes;
[[maybe_unused]] bool negative_offset = false;
if (!lexer.is_eof()) {
if (lexer.consume_specific('Z'))
goto done_parsing;
@ -233,7 +234,6 @@ done_parsing:;
if (offset_hours.has_value() || offset_minutes.has_value())
dbgln("FIXME: Implement GeneralizedTime with offset!");
// Unceremoniously drop the milliseconds on the floor.
return Core::DateTime::create(year.value(), month.value(), day.value(), hour.value(), minute.value_or(0), seconds.value_or(0));
return UnixDateTime::from_unix_time_parts(year.value(), month.value(), day.value(), hour.value(), minute.value_or(0), seconds.value_or(0), milliseconds.value_or(0));
}
}

View file

@ -6,8 +6,8 @@
#pragma once
#include <AK/Time.h>
#include <AK/Types.h>
#include <LibCore/DateTime.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto::ASN1 {
@ -75,7 +75,7 @@ ByteString kind_name(Kind);
ByteString class_name(Class);
ByteString type_name(Type);
Optional<Core::DateTime> parse_utc_time(StringView);
Optional<Core::DateTime> parse_generalized_time(StringView);
Optional<UnixDateTime> parse_utc_time(StringView);
Optional<UnixDateTime> parse_generalized_time(StringView);
}

View file

@ -296,7 +296,7 @@ static ErrorOr<RelativeDistinguishedName> parse_name(Crypto::ASN1::Decoder& deco
return rdn;
}
static ErrorOr<Core::DateTime> parse_time(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
static ErrorOr<UnixDateTime> parse_time(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
{
// Time ::= Choice {
// utc_time UTCTime,

View file

@ -9,9 +9,9 @@
#include <AK/ByteBuffer.h>
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <AK/Time.h>
#include <AK/Types.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/DateTime.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibCrypto/PK/RSA.h>
#include <LibTLS/Extensions.h>
@ -233,8 +233,8 @@ private:
};
struct Validity {
Core::DateTime not_before;
Core::DateTime not_after;
UnixDateTime not_before;
UnixDateTime not_after;
};
class SubjectPublicKey {

View file

@ -104,15 +104,15 @@ void TLSv12::consume(ReadonlyBytes record)
bool Certificate::is_valid() const
{
auto now = Core::DateTime::now();
auto now = UnixDateTime::now();
if (now < validity.not_before) {
dbgln("certificate expired (not yet valid, signed for {})", validity.not_before.to_byte_string());
dbgln("certificate expired (not yet valid, signed for {})", Core::DateTime::from_timestamp(validity.not_before.seconds_since_epoch()));
return false;
}
if (validity.not_after < now) {
dbgln("certificate expired (expiry date {})", validity.not_after.to_byte_string());
dbgln("certificate expired (expiry date {})", Core::DateTime::from_timestamp(validity.not_after.seconds_since_epoch()));
return false;
}