From 80925cf84f73a04a2817b83f9ef31207831c7397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Tue, 14 Nov 2023 12:56:19 +0100 Subject: [PATCH] ASN1: speed up parsing of certificate timestamps By parsing QDate and QTime separately it will internally parse the values as-if-by UTC. This means we avoid the overhead of figuring out what the local timezone is repeatedly for each certificate. On Windows, with Schannel, this brings QAsn1Element::toDateTime from consuming more than 97% of the test time to below 10%. The test being tst_QSslSocket::systemCaCertificates. It also goes from taking 1 minute in debug, to 1 second. As a drive-by: add a (currently) failing test for Feb 29 2000, which fails because we decode the date as 1900 before adjusting it to 2000. But there is no 1900-02-29, so it returns an invalid date. As spotted by Eddy. Pick-to: 6.5 Change-Id: Iefa73a01d710129faf6412c4fa8bc2b5d7c98bbb Reviewed-by: Timur Pocheptsov Reviewed-by: Edward Welbourne (cherry picked from commit 731b759c00ce17072e3f93fdd7044490e51171ca) --- src/plugins/tls/shared/qasn1element.cpp | 23 ++++++++++--------- .../ssl/qasn1element/tst_qasn1element.cpp | 6 +++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/plugins/tls/shared/qasn1element.cpp b/src/plugins/tls/shared/qasn1element.cpp index 507a3a727a5..8128cf6f58f 100644 --- a/src/plugins/tls/shared/qasn1element.cpp +++ b/src/plugins/tls/shared/qasn1element.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -224,15 +225,10 @@ QDateTime QAsn1Element::toDateTime() const return result; if (mType == UtcTimeType && mValue.size() == 13) { - result = QDateTime::fromString(QString::fromLatin1(mValue), - QStringLiteral("yyMMddHHmmsst")); - if (!result.isValid()) + const QLatin1StringView inputView(mValue); + QDate date = QDate::fromString(inputView.first(6), u"yyMMdd"); + if (!date.isValid()) return result; - - Q_ASSERT(result.timeSpec() == Qt::UTC); - - QDate date = result.date(); - // RFC 2459: // Where YY is greater than or equal to 50, the year shall be // interpreted as 19YY; and @@ -242,10 +238,15 @@ QDateTime QAsn1Element::toDateTime() const // QDateTime interprets the 'yy' format as 19yy, so we may need to adjust // the year (bring it in the [1950, 2049] range). if (date.year() < 1950) - result.setDate(date.addYears(100)); + date = date.addYears(100); - Q_ASSERT(result.date().year() >= 1950); - Q_ASSERT(result.date().year() <= 2049); + Q_ASSERT(date.year() >= 1950); + Q_ASSERT(date.year() <= 2049); + + QTime time = QTime::fromString(inputView.sliced(6, 6), u"HHmmss"); + if (!time.isValid()) + return result; + result = QDateTime(date, time, QTimeZone::UTC); } else if (mType == GeneralizedTimeType && mValue.size() == 15) { result = QDateTime::fromString(QString::fromLatin1(mValue), QStringLiteral("yyyyMMddHHmmsst")); diff --git a/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp index eb614551a9e..aa126f0f1a5 100644 --- a/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp +++ b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp @@ -121,6 +121,9 @@ void tst_QAsn1Element::dateTime_data() QTest::newRow("UTCTime - year 2000") << QByteArray::fromHex("170d3030313232343035353530305a") << QDateTime(QDate(2000, 12, 24), QTime(5, 55), QTimeZone::UTC); + QTest::newRow("UTCTime - leap day year 2000") + << QByteArray::fromHex("170d3030303232393035353530305a") + << QDateTime(QDate(2000, 2, 29), QTime(5, 55), QTimeZone::UTC); QTest::newRow("UTCTime - year 2049") << QByteArray::fromHex("170d3439313232343035353530305a") << QDateTime(QDate(2049, 12, 24), QTime(5, 55), QTimeZone::UTC); @@ -163,6 +166,9 @@ void tst_QAsn1Element::dateTime() QAsn1Element elem; QVERIFY(elem.read(encoded)); + QEXPECT_FAIL("UTCTime - leap day year 2000", + "We decode as 1900, and then adjust to 2000. But there was no leap day in 1900!", + Continue); QCOMPARE(elem.toDateTime(), value); }