From c5c06ce665dac872b2f978150780144b6297bfb7 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Sun, 9 Nov 2025 20:24:47 +0100 Subject: [PATCH] Do not include any secret padding in the otpauth URI The IETF draft-linuxgemini-otpauth-uri-02 recommends to not include the padding in Section 3.3.1. cf. https://www.ietf.org/archive/id/draft-linuxgemini-otpauth-uri-02.html#section-3.3.1 (fixes issue #12540) Signed-off-by: Sven Strickroth --- src/core/Base32.cpp | 5 ++++- src/core/Base32.h | 2 +- src/core/Totp.cpp | 2 +- tests/TestTotp.cpp | 28 ++++++++++++++++++++++++++++ tests/TestTotp.h | 1 + 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/core/Base32.cpp b/src/core/Base32.cpp index d0a148eec..6529bb6c1 100644 --- a/src/core/Base32.cpp +++ b/src/core/Base32.cpp @@ -267,7 +267,7 @@ QByteArray Base32::removePadding(const QByteArray& encodedData) return newEncodedData; } -QByteArray Base32::sanitizeInput(const QByteArray& encodedData) +QByteArray Base32::sanitizeInput(const QByteArray& encodedData, bool withPadding /* = true */) { if (encodedData.size() <= 0) { return encodedData; @@ -294,5 +294,8 @@ QByteArray Base32::sanitizeInput(const QByteArray& encodedData) } newEncodedData.resize(i); + if (!withPadding) + return removePadding(newEncodedData); + return addPadding(newEncodedData); } diff --git a/src/core/Base32.h b/src/core/Base32.h index 07b16a0cc..dee424625 100644 --- a/src/core/Base32.h +++ b/src/core/Base32.h @@ -35,7 +35,7 @@ public: Q_REQUIRED_RESULT static QByteArray encode(const QByteArray&); Q_REQUIRED_RESULT static QByteArray addPadding(const QByteArray&); Q_REQUIRED_RESULT static QByteArray removePadding(const QByteArray&); - Q_REQUIRED_RESULT static QByteArray sanitizeInput(const QByteArray&); + Q_REQUIRED_RESULT static QByteArray sanitizeInput(const QByteArray&, bool withPadding = true); }; #endif // BASE32_H diff --git a/src/core/Totp.cpp b/src/core/Totp.cpp index ed15a9fb8..0275423e4 100644 --- a/src/core/Totp.cpp +++ b/src/core/Totp.cpp @@ -180,7 +180,7 @@ QString Totp::writeSettings(const QSharedPointer& settings, auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1") .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)), username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)), - QString(QUrl::toPercentEncoding(Base32::sanitizeInput(settings->key.toLatin1()))), + QString(QUrl::toPercentEncoding(Base32::sanitizeInput(settings->key.toLatin1(), !forceOtp))), QString::number(settings->step), QString::number(settings->digits)); diff --git a/tests/TestTotp.cpp b/tests/TestTotp.cpp index 13aaf9a2d..6b7008750 100644 --- a/tests/TestTotp.cpp +++ b/tests/TestTotp.cpp @@ -107,6 +107,34 @@ void TestTotp::testParseSecret() QVERIFY(settings.isNull()); } +void TestTotp::testTotpWriteSettings() +{ + auto settings1 = Totp::createSettings("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP); + QCOMPARE( + Totp::writeSettings(settings1, "ACME Co", "john", true), + "otpauth://totp/ACME%20Co:john?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=30&digits=6&issuer=ACME%20Co"); + + auto settings2 = Totp::createSettings("63BEDWCQZKTQWPESARIERL5DTTQFCJTK", 3, 25); + QCOMPARE( + Totp::writeSettings(settings2, "ACME Co", "", true), + "otpauth://totp/ACME%20Co:none?secret=63BEDWCQZKTQWPESARIERL5DTTQFCJTK&period=25&digits=3&issuer=ACME%20Co"); + + auto settings3 = Totp::createSettings("HXDMVJECJJWSRBY", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP); + QCOMPARE( + Totp::writeSettings(settings3, "", "john", true), + "otpauth://totp/KeePassXC:john?secret=HXDMVJECJJWSRBY&period=30&digits=6&issuer=KeePassXC"); + + auto settings4 = Totp::createSettings("HXDMVJECJJWSRBY=", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP); + QCOMPARE( + Totp::writeSettings(settings4, "NoPadding", "john", true), + "otpauth://totp/NoPadding:john?secret=HXDMVJECJJWSRBY&period=30&digits=6&issuer=NoPadding"); + + auto settings5 = Totp::createSettings("HXDMVJECJJWSRBY=", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP); + QCOMPARE( + Totp::writeSettings(settings5, "WithPadding", "john"), + "otpauth://totp/WithPadding:john?secret=HXDMVJECJJWSRBY%3D&period=30&digits=6&issuer=WithPadding"); +} + void TestTotp::testTotpCode() { // Test vectors from RFC 6238 diff --git a/tests/TestTotp.h b/tests/TestTotp.h index f2e696734..670f15597 100644 --- a/tests/TestTotp.h +++ b/tests/TestTotp.h @@ -28,6 +28,7 @@ class TestTotp : public QObject private slots: void initTestCase(); void testParseSecret(); + void testTotpWriteSettings(); void testTotpCode(); void testSteamTotp(); void testEntryHistory();