File indexing completed on 2024-04-28 05:50:05
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 #include "uri/uri.h" 0006 0007 #include <QTest> 0008 #include <QtDebug> 0009 0010 Q_DECLARE_METATYPE(uri::QrParts::Type); 0011 0012 class UriParsingTest: public QObject // clazy:exclude=ctor-missing-parent-argument 0013 { 0014 Q_OBJECT 0015 private Q_SLOTS: 0016 void testValid(void); 0017 void testValid_data(void); 0018 void testInvalid(void); 0019 void testInvalid_data(void); 0020 }; 0021 0022 static void define_valid_test_data(void) 0023 { 0024 QTest::addColumn<QString>("input"); 0025 QTest::addColumn<uri::QrParts::Type>("type"); 0026 QTest::addColumn<QString>("name"); 0027 QTest::addColumn<QString>("issuer"); 0028 QTest::addColumn<QString>("secret"); 0029 QTest::addColumn<QString>("timeStep"); 0030 QTest::addColumn<QString>("tokenLength"); 0031 QTest::addColumn<QString>("algorithm"); 0032 QTest::addColumn<QString>("counter"); 0033 } 0034 0035 static void define_valid_test_case(const char *testCase, const QString &input, uri::QrParts::Type type, 0036 const QString &name, const QString &issuer, const QString &secret, 0037 const QString &timeStep, const QString &tokenLength, const QString &algorithm, 0038 const QString &counter) 0039 { 0040 QTest::newRow(testCase) << input 0041 << type << name << issuer << secret << timeStep << tokenLength << algorithm << counter; 0042 } 0043 0044 void UriParsingTest::testValid(void) 0045 { 0046 QFETCH(QString, input); 0047 0048 const std::optional<uri::QrParts> result = uri::QrParts::parse(input); 0049 QVERIFY2(result, "should parse valid input successfully"); 0050 QTEST(result->type(), "type"); 0051 QTEST(result->name(), "name"); 0052 QTEST(result->issuer(), "issuer"); 0053 QTEST(result->secret(), "secret"); 0054 QTEST(result->tokenLength(), "tokenLength"); 0055 QTEST(result->algorithm(), "algorithm"); 0056 QTEST(result->counter(), "counter"); 0057 } 0058 0059 void UriParsingTest::testValid_data(void) 0060 { 0061 define_valid_test_data(); 0062 define_valid_test_case("hotp (all fields)", 0063 QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"), 0064 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0065 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0066 QStringLiteral("42")); 0067 define_valid_test_case("hotp (all fields, URI encoded label separator)", 0068 QStringLiteral("otpauth://hotp/issuer%3Ana:me?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"), 0069 uri::QrParts::Type::Hotp, QStringLiteral("na:me"), QStringLiteral("issuer"), 0070 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0071 QStringLiteral("42")); 0072 define_valid_test_case("hotp (minimal)", 0073 QStringLiteral("otpauth://hotp/name?secret=VALUE&counter=42"), 0074 uri::QrParts::Type::Hotp, QStringLiteral("name"), QString(), QStringLiteral("VALUE"), 0075 QString(), QString(), QString(), QStringLiteral("42")); 0076 define_valid_test_case("hotp (minimal, without name and missing params)", 0077 QStringLiteral("otpauth://hotp?secret=VALUE"), 0078 uri::QrParts::Type::Hotp, QString(), QString(), QStringLiteral("VALUE"), QString(), 0079 QString(), QString(), QString()); 0080 define_valid_test_case("hotp (issuer only in label)", 0081 QStringLiteral("otpauth://hotp/issuer:name?secret=VALUE&counter=42"), 0082 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0083 QStringLiteral("VALUE"), QString(), QString(), QString(), QStringLiteral("42")); 0084 define_valid_test_case("hotp (issuer only as param)", 0085 QStringLiteral("otpauth://hotp/name?secret=VALUE&counter=42&issuer=issuer"), 0086 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0087 QStringLiteral("VALUE"), QString(), QString(), QString(), QStringLiteral("42")); 0088 define_valid_test_case("hotp (inconsistent issuer)", 0089 QStringLiteral("otpauth://hotp/issuer:name?secret=VALUE&counter=42&issuer=other"), 0090 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0091 QStringLiteral("VALUE"), QString(), QString(), QString(), QStringLiteral("42")); 0092 define_valid_test_case("hotp (empty counter)", 0093 QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter="), 0094 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0095 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0096 QString()); 0097 define_valid_test_case("hotp (missing counter)", 0098 QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1"), 0099 uri::QrParts::Type::Hotp, QStringLiteral("name"), QStringLiteral("issuer"), 0100 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0101 QString()); 0102 define_valid_test_case("totp (all fields, including redundant counter)", 0103 QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"), 0104 uri::QrParts::Type::Totp, QStringLiteral("name"), QStringLiteral("issuer"), 0105 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0106 QStringLiteral("42")); 0107 define_valid_test_case("totp (all fields, except redundant counter)", 0108 QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1"), 0109 uri::QrParts::Type::Totp, QStringLiteral("name"), QStringLiteral("issuer"), 0110 QStringLiteral("VALUE"), QStringLiteral("30"), QStringLiteral("6"), QStringLiteral("sha1"), 0111 QString()); 0112 define_valid_test_case("totp (minimal)", 0113 QStringLiteral("otpauth://totp/name?secret=VALUE"), 0114 uri::QrParts::Type::Totp, QStringLiteral("name"), QString(), QStringLiteral("VALUE"), QString(), 0115 QString(), QString(), QString()); 0116 define_valid_test_case("totp (minimal, without name)", 0117 QStringLiteral("otpauth://totp?secret=VALUE"), 0118 uri::QrParts::Type::Totp, QString(), QString(), QStringLiteral("VALUE"), QString(), 0119 QString(), QString(), QString()); 0120 define_valid_test_case("hotp (with padding in secret)", 0121 QStringLiteral("otpauth://hotp?secret=VALUE==="), 0122 uri::QrParts::Type::Hotp, QString(), QString(), QStringLiteral("VALUE==="), QString(), 0123 QString(), QString(), QString()); 0124 define_valid_test_case("totp (with padding in secret)", 0125 QStringLiteral("otpauth://totp?secret=VALUE===&period=30"), 0126 uri::QrParts::Type::Totp, QString(), QString(), QStringLiteral("VALUE==="), 0127 QStringLiteral("30"), QString(), QString(), QString()); 0128 define_valid_test_case("totp (Microsoft organization account)", 0129 QStringLiteral("otpauth://totp/Mega%20Corporation%20A.B.%20%26%20Co.%20Kg%3Auser%40domain?secret=abcdef&issuer=Microsoft"), 0130 uri::QrParts::Type::Totp, QStringLiteral("user@domain"), QStringLiteral("Mega Corporation A.B. & Co. Kg"), QStringLiteral("abcdef"), 0131 QString(), QString(), QString(), QString()); 0132 } 0133 0134 void UriParsingTest::testInvalid(void) 0135 { 0136 QFETCH(QString, input); 0137 QVERIFY2(!uri::QrParts::parse(input), "should reject invalid input"); 0138 } 0139 0140 void UriParsingTest::testInvalid_data(void) 0141 { 0142 QTest::addColumn<QString>("input"); 0143 0144 QTest::newRow("wrong scheme") << QStringLiteral("http://localhost"); 0145 QTest::newRow("missing type") << QStringLiteral("otpauth:///issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0146 QTest::newRow("unsupported type") << QStringLiteral("otpauth://wrong/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0147 QTest::newRow("invalid param") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42&foo=bar"); 0148 QTest::newRow("missing secret") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&period=30&digits=6&algorithm=sha1&counter=42"); 0149 QTest::newRow("empty secret") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=&period=30&digits=6&algorithm=sha1&counter=42"); 0150 QTest::newRow("secret = bad utf8") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=%c0%7fALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0151 QTest::newRow("secret = invalid percent encoding") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=%VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0152 QTest::newRow("counter = bad utf8") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=%fc%80%80%80%80%80%80"); 0153 QTest::newRow("counter = invalid percent encoding") << QStringLiteral("otpauth://hotp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42%"); 0154 QTest::newRow("name label = bad utf8") << QStringLiteral("otpauth://hotp/issuer:name%c1%00?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0155 QTest::newRow("name label = invalid percent encoding") << QStringLiteral("otpauth://hotp/issuer:n%#ame?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0156 QTest::newRow("issuer label = bad utf8") << QStringLiteral("otpauth://hotp/issuer%90:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0157 QTest::newRow("issuer label = invalid percent encoding") << QStringLiteral("otpauth://hotp/is%suer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha1&counter=42"); 0158 QTest::newRow("issuer param = bad utf8") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer%cf&secret=VALUE&period=30&digits=%80&algorithm=sha1"); 0159 QTest::newRow("issuer param = invalid percent encoding") << QStringLiteral("otpauth://totp/issuer:name?issuer=issu%er&secret=VALUE&period=30&digits=%S&algorithm=sha1"); 0160 QTest::newRow("digits = bad utf8") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=%80&algorithm=sha1"); 0161 QTest::newRow("digits = invalid percent encoding") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=%S&algorithm=sha1"); 0162 QTest::newRow("period = bad utf8") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=%c0&digits=6&algorithm=sha1"); 0163 QTest::newRow("period = invalid percent encoding") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=3%&digits=6&algorithm=sha1"); 0164 QTest::newRow("algorithm = bad utf8") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha%ff"); 0165 QTest::newRow("algorithm = invalid percent encoding") << QStringLiteral("otpauth://totp/issuer:name?issuer=issuer&secret=VALUE&period=30&digits=6&algorithm=sha%1"); 0166 QTest::newRow("hotp without params") << QStringLiteral("otpauth://hotp/issuer:name"); 0167 QTest::newRow("totp without params") << QStringLiteral("otpauth://hotp/name"); 0168 } 0169 0170 QTEST_APPLESS_MAIN(UriParsingTest) 0171 0172 #include "qr-parsing.moc"