File indexing completed on 2024-05-12 05:52:46

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  */
0005 #include "account/actions_p.h"
0006 
0007 #include "../test-utils/secret.h"
0008 #include "../../test-utils/spy.h"
0009 
0010 #include <QSignalSpy>
0011 #include <QTest>
0012 
0013 class ComputeTotpTest: public QObject
0014 {
0015     Q_OBJECT
0016 private Q_SLOTS:
0017     void initTestCase(void);
0018     void testDefaults(void);
0019     void testDefaults_data(void);
0020 private:
0021     accounts::AccountSecret m_secret;
0022 };
0023 
0024 // RFC test vector uses the key: 12345678901234567890
0025 static QByteArray rfcSecret("12345678901234567890");
0026 
0027 // the RFC test vector consists of 6-character tokens
0028 static uint tokenLength = 6;
0029 
0030 // the default TOTP timestep is 30s
0031 static uint timeStep = 30;
0032 
0033 // the default TOTP epoch is the Unix epoch
0034 static QDateTime epoch = QDateTime::fromMSecsSinceEpoch(0);
0035 
0036 void ComputeTotpTest::initTestCase(void)
0037 {
0038     QVERIFY2(test::useDummyPassword(&m_secret), "should be able to set up the master key");
0039 }
0040 
0041 void ComputeTotpTest::testDefaults(void)
0042 {
0043     QFETCH(qint64, counter);
0044     std::function<qint64(void)> clock([counter](void) -> qint64 {
0045         return counter * timeStep * 1000;
0046     });
0047 
0048 
0049 
0050     std::optional<secrets::EncryptedSecret> tokenSecret = test::encrypt(&m_secret, rfcSecret);
0051     QVERIFY2(tokenSecret, "should be able to encrypt the token secret");
0052 
0053     accounts::ComputeTotp uut(&m_secret, *tokenSecret, tokenLength, epoch, timeStep, accounts::Account::Hash::Sha1, clock);
0054     QSignalSpy tokenGenerated(&uut, &accounts::ComputeTotp::otp);
0055     QSignalSpy jobFinished(&uut, &accounts::ComputeTotp::finished);
0056 
0057     uut.run();
0058 
0059     QVERIFY2(test::signal_eventually_emitted_once(tokenGenerated), "token should be generated by now");
0060     QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished by now");
0061 
0062     QTEST(tokenGenerated.at(0).at(0).toString(), "rfc-test-vector");
0063     QCOMPARE(tokenGenerated.at(0).at(2).toDateTime(), QDateTime::fromMSecsSinceEpoch(timeStep * 1000 * (counter + 1))); // from
0064     QCOMPARE(tokenGenerated.at(0).at(3).toDateTime(), QDateTime::fromMSecsSinceEpoch(timeStep * 1000 * (counter + 2))); // until
0065 }
0066 
0067 static void define_test_case(int k, const char *expected)
0068 {
0069 
0070     QByteArray output(expected, tokenLength);
0071     QTest::newRow(qPrintable(QStringLiteral("RFC 4226 test vector, # time steps = %1").arg(k))) << (qint64) k << QString::fromLocal8Bit(output);
0072 }
0073 
0074 void ComputeTotpTest::testDefaults_data(void)
0075 {
0076     static const char * corpus[10] {
0077         "755224",
0078         "287082",
0079         "359152",
0080         "969429",
0081         "338314",
0082         "254676",
0083         "287922",
0084         "162583",
0085         "399871",
0086         "520489"
0087     };
0088 
0089     QTest::addColumn<qint64>("counter");
0090     QTest::addColumn<QString>("rfc-test-vector");
0091 
0092     for (int k = 0; k < 10; ++k) {
0093         define_test_case(k, corpus[k]);
0094     }
0095 }
0096 
0097 QTEST_MAIN(ComputeTotpTest)
0098 
0099 #include "compute-totp.moc"