File indexing completed on 2024-05-12 05:52:47
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 #include "account/keys.h" 0006 0007 #include "../../secrets/test-utils/random.h" 0008 #include "../../test-utils/spy.h" 0009 0010 #include <QSignalSpy> 0011 #include <QTest> 0012 0013 #include <cstring> 0014 0015 static QByteArray fill(int size) 0016 { 0017 QByteArray a; 0018 a.resize(size); 0019 /* 0020 * Because this value is used to generate the expected challenge value(s) up front, this salt has to match the 0021 * behaviour of test::fakeRandom() in case of 'new' passwords where the actual salt will be drawn 'randomly' 0022 * (from test::fakeRandom()). 0023 */ 0024 a.fill('A', -1); 0025 return a; 0026 } 0027 0028 static QByteArray salt() 0029 { 0030 return fill(crypto_pwhash_SALTBYTES); 0031 } 0032 0033 static QByteArray masterPassword(void) 0034 { 0035 static QByteArray MASTER_PASSWORD("hello, world"); 0036 return MASTER_PASSWORD; 0037 } 0038 0039 static secrets::SecureMemory * secret(void) 0040 { 0041 const auto master = masterPassword(); 0042 size_t size = (size_t) master.size(); 0043 auto memory = secrets::SecureMemory::allocate(size); 0044 if (memory) { 0045 std::memcpy(memory->data(), master.constData(), size); 0046 } 0047 return memory; 0048 } 0049 0050 static std::optional<secrets::KeyDerivationParameters> keyParams = secrets::KeyDerivationParameters::create( 0051 crypto_secretbox_KEYBYTES, crypto_pwhash_ALG_DEFAULT, crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_OPSLIMIT_MIN 0052 ); 0053 0054 static secrets::SecureMasterKey * key(secrets::SecureMemory *password) 0055 { 0056 if (!keyParams) { 0057 qDebug() << "Unable to setup() dummy master key to generate test data with"; 0058 return nullptr; 0059 } 0060 return secrets::SecureMasterKey::derive(password, *keyParams, salt(), &test::fakeRandom); 0061 } 0062 0063 static std::optional<secrets::EncryptedSecret> challenge(void) 0064 { 0065 QScopedPointer<secrets::SecureMemory> s(secret()); 0066 if (!s) { 0067 qDebug() << "Unable to generate challenge(), unable to allocate buffer for password secret."; 0068 return std::nullopt; 0069 } 0070 0071 QScopedPointer<secrets::SecureMasterKey> k(key(s.data())); 0072 if (!k) { 0073 qDebug() << "Unable to generate challenge(), unable to setup dummy master key."; 0074 return std::nullopt; 0075 } 0076 return k->encrypt(s.data()); 0077 } 0078 0079 class PasswordFlowTest : public QObject // clazy:exclude=ctor-missing-parent-argument 0080 { 0081 Q_OBJECT 0082 private Q_SLOTS: 0083 void initTestCase(void); 0084 void supplyExistingPassword(void); 0085 void cancelExistingPassword(void); 0086 void supplyNewPassword(void); 0087 void cancelNewPassword(void); 0088 void retryExistingPassword(void); 0089 private: 0090 QByteArray m_salt = salt(); 0091 std::optional<secrets::EncryptedSecret> m_challenge = challenge(); 0092 }; 0093 0094 void PasswordFlowTest::initTestCase(void) 0095 { 0096 QVERIFY2(keyParams, "should be able to construct key derivation parameters"); 0097 QVERIFY2(m_challenge, "should be able to construct password challenge"); 0098 qDebug() << "Running with challenge:" << m_challenge->cryptText().toBase64() << "nonce:" << m_challenge->nonce().toBase64(); 0099 } 0100 0101 void PasswordFlowTest::supplyNewPassword(void) 0102 { 0103 accounts::AccountSecret uut(&test::fakeRandom); 0104 QSignalSpy existingPasswordNeeded(&uut, &accounts::AccountSecret::existingPasswordNeeded); 0105 QSignalSpy newPasswordNeeded(&uut, &accounts::AccountSecret::newPasswordNeeded); 0106 QSignalSpy passwordAvailable(&uut, &accounts::AccountSecret::passwordAvailable); 0107 QSignalSpy requestsCancelled(&uut, &accounts::AccountSecret::requestsCancelled); 0108 QSignalSpy keyAvailable(&uut, &accounts::AccountSecret::keyAvailable); 0109 QSignalSpy keyFailed(&uut, &accounts::AccountSecret::keyFailed); 0110 0111 // check correct initial state is reported 0112 QCOMPARE(uut.isStillAlive(), true); 0113 QCOMPARE(uut.isNewPasswordRequested(), false); 0114 QCOMPARE(uut.isExistingPasswordRequested(), false); 0115 QCOMPARE(uut.isPasswordAvailable(), false); 0116 QCOMPARE(uut.isKeyAvailable(), false); 0117 QCOMPARE(uut.key(), nullptr); 0118 QVERIFY2(!uut.challenge(), "should not have a (generated) password challenge yet"); 0119 0120 // advance the state: request password 0121 QVERIFY2(uut.requestNewPassword(), "should be able to request a (new) password"); 0122 QVERIFY2(test::signal_eventually_emitted_once(newPasswordNeeded), "request for (new) password should be signalled"); 0123 0124 // check the state is correctly updated 0125 QCOMPARE(uut.isStillAlive(), true); 0126 QCOMPARE(uut.isNewPasswordRequested(), true); 0127 QCOMPARE(uut.isExistingPasswordRequested(), false); 0128 QCOMPARE(uut.isPasswordAvailable(), false); 0129 QCOMPARE(uut.isKeyAvailable(), false); 0130 QCOMPARE(uut.key(), nullptr); 0131 QVERIFY2(!uut.challenge(), "should not have a (generated) password challenge yet"); 0132 0133 QCOMPARE(newPasswordNeeded.count(), 1); 0134 QCOMPARE(passwordAvailable.count(), 0); 0135 QCOMPARE(existingPasswordNeeded.count(), 0); 0136 QCOMPARE(keyAvailable.count(), 0); 0137 QCOMPARE(keyFailed.count(), 0); 0138 QCOMPARE(requestsCancelled.count(), 0); 0139 0140 // advance the state: supply password 0141 QString password = QString::fromUtf8(masterPassword()); 0142 QString wiped = QStringLiteral("*").repeated(password.size()); 0143 0144 QVERIFY2(uut.answerNewPassword(password, *keyParams), "(new) password should be accepted"); 0145 QVERIFY2(test::signal_eventually_emitted_once(passwordAvailable), "availability of the (new) password should be signalled"); 0146 QCOMPARE(password, wiped); 0147 0148 // check the state is correctly updated 0149 QCOMPARE(uut.isStillAlive(), true); 0150 QCOMPARE(uut.isNewPasswordRequested(), true); 0151 QCOMPARE(uut.isExistingPasswordRequested(), false); 0152 QCOMPARE(uut.isPasswordAvailable(), true); 0153 QCOMPARE(uut.isKeyAvailable(), false); 0154 QCOMPARE(uut.key(), nullptr); 0155 QVERIFY2(!uut.challenge(), "should still not have a (generated) password challenge yet"); 0156 0157 QCOMPARE(newPasswordNeeded.count(), 1); 0158 QCOMPARE(passwordAvailable.count(), 1); 0159 QCOMPARE(existingPasswordNeeded.count(), 0); 0160 QCOMPARE(keyAvailable.count(), 0); 0161 QCOMPARE(keyFailed.count(), 0); 0162 QCOMPARE(requestsCancelled.count(), 0); 0163 0164 // advance the state: derive the master key 0165 QVERIFY2(uut.deriveKey(), "key derivation should succeed"); 0166 QVERIFY2(test::signal_eventually_emitted_once(keyAvailable), "availability of the master key should be signalled"); 0167 0168 // check the state is correctly updated 0169 QCOMPARE(uut.isStillAlive(), true); 0170 QCOMPARE(uut.isNewPasswordRequested(), true); 0171 QCOMPARE(uut.isExistingPasswordRequested(), false); 0172 QCOMPARE(uut.isPasswordAvailable(), false); 0173 QCOMPARE(uut.isKeyAvailable(), true); 0174 QVERIFY2(uut.key(), "should have a master key by now"); 0175 const auto generatedChallenge = uut.challenge(); 0176 QVERIFY2(generatedChallenge, "should have a (generated) password challenge by now"); 0177 QCOMPARE(generatedChallenge->cryptText(), m_challenge->cryptText()); 0178 QCOMPARE(generatedChallenge->nonce(), m_challenge->nonce()); 0179 0180 QCOMPARE(newPasswordNeeded.count(), 1); 0181 QCOMPARE(passwordAvailable.count(), 1); 0182 QCOMPARE(existingPasswordNeeded.count(), 0); 0183 QCOMPARE(keyAvailable.count(), 1); 0184 QCOMPARE(keyFailed.count(), 0); 0185 QCOMPARE(requestsCancelled.count(), 0); 0186 } 0187 0188 void PasswordFlowTest::cancelNewPassword(void) 0189 { 0190 accounts::AccountSecret uut(&test::fakeRandom); 0191 QSignalSpy existingPasswordNeeded(&uut, &accounts::AccountSecret::existingPasswordNeeded); 0192 QSignalSpy newPasswordNeeded(&uut, &accounts::AccountSecret::newPasswordNeeded); 0193 QSignalSpy passwordAvailable(&uut, &accounts::AccountSecret::passwordAvailable); 0194 QSignalSpy requestsCancelled(&uut, &accounts::AccountSecret::requestsCancelled); 0195 QSignalSpy keyAvailable(&uut, &accounts::AccountSecret::keyAvailable); 0196 QSignalSpy keyFailed(&uut, &accounts::AccountSecret::keyFailed); 0197 0198 // check correct initial state is reported 0199 QCOMPARE(uut.isStillAlive(), true); 0200 QCOMPARE(uut.isNewPasswordRequested(), false); 0201 QCOMPARE(uut.isExistingPasswordRequested(), false); 0202 QCOMPARE(uut.isPasswordAvailable(), false); 0203 QCOMPARE(uut.isKeyAvailable(), false); 0204 QCOMPARE(uut.key(), nullptr); 0205 QVERIFY2(!uut.challenge(), "should not have a (generated) password challenge yet"); 0206 0207 // advance the state: request password 0208 QVERIFY2(uut.requestNewPassword(), "should be able to request a (new) password"); 0209 QVERIFY2(test::signal_eventually_emitted_once(newPasswordNeeded), "request for (new) password should be signalled"); 0210 0211 // check the state is correctly updated 0212 QCOMPARE(uut.isStillAlive(), true); 0213 QCOMPARE(uut.isNewPasswordRequested(), true); 0214 QCOMPARE(uut.isExistingPasswordRequested(), false); 0215 QCOMPARE(uut.isPasswordAvailable(), false); 0216 QCOMPARE(uut.isKeyAvailable(), false); 0217 QCOMPARE(uut.key(), nullptr); 0218 QVERIFY2(!uut.challenge(), "should still not have a (generated) password challenge yet"); 0219 0220 QCOMPARE(newPasswordNeeded.count(), 1); 0221 QCOMPARE(passwordAvailable.count(), 0); 0222 QCOMPARE(existingPasswordNeeded.count(), 0); 0223 QCOMPARE(keyAvailable.count(), 0); 0224 QCOMPARE(keyFailed.count(), 0); 0225 QCOMPARE(requestsCancelled.count(), 0); 0226 0227 // advance the state: cancel the request 0228 uut.cancelRequests(); 0229 QVERIFY2(test::signal_eventually_emitted_once(requestsCancelled), "requests for (new) password should be cancelled by now"); 0230 0231 // check the state is correctly updated 0232 QCOMPARE(uut.isStillAlive(), false); 0233 QCOMPARE(uut.isNewPasswordRequested(), true); 0234 QCOMPARE(uut.isExistingPasswordRequested(), false); 0235 QCOMPARE(uut.isPasswordAvailable(), false); 0236 QCOMPARE(uut.isKeyAvailable(), false); 0237 QCOMPARE(uut.key(), nullptr); 0238 QVERIFY2(!uut.challenge(), "should still not acknowledge a (generated) password challenge"); 0239 0240 QCOMPARE(newPasswordNeeded.count(), 1); 0241 QCOMPARE(passwordAvailable.count(), 0); 0242 QCOMPARE(existingPasswordNeeded.count(), 0); 0243 QCOMPARE(keyAvailable.count(), 0); 0244 QCOMPARE(keyFailed.count(), 0); 0245 QCOMPARE(requestsCancelled.count(), 1); 0246 } 0247 0248 void PasswordFlowTest::cancelExistingPassword(void) 0249 { 0250 accounts::AccountSecret uut; 0251 QSignalSpy existingPasswordNeeded(&uut, &accounts::AccountSecret::existingPasswordNeeded); 0252 QSignalSpy newPasswordNeeded(&uut, &accounts::AccountSecret::newPasswordNeeded); 0253 QSignalSpy passwordAvailable(&uut, &accounts::AccountSecret::passwordAvailable); 0254 QSignalSpy requestsCancelled(&uut, &accounts::AccountSecret::requestsCancelled); 0255 QSignalSpy keyAvailable(&uut, &accounts::AccountSecret::keyAvailable); 0256 QSignalSpy keyFailed(&uut, &accounts::AccountSecret::keyFailed); 0257 0258 // check correct initial state is reported 0259 QCOMPARE(uut.isStillAlive(), true); 0260 QCOMPARE(uut.isNewPasswordRequested(), false); 0261 QCOMPARE(uut.isExistingPasswordRequested(), false); 0262 QCOMPARE(uut.isPasswordAvailable(), false); 0263 QCOMPARE(uut.isKeyAvailable(), false); 0264 QCOMPARE(uut.key(), nullptr); 0265 QVERIFY2(!uut.challenge(), "should not have a password challenge yet"); 0266 0267 // advance the state: request password 0268 QVERIFY2(uut.requestExistingPassword(*m_challenge, m_salt, *keyParams), "should be able to request a (existing) password"); 0269 QVERIFY2(test::signal_eventually_emitted_once(existingPasswordNeeded), "request for (existing) password should be signalled"); 0270 0271 // check the state is correctly updated 0272 QCOMPARE(uut.isStillAlive(), true); 0273 QCOMPARE(uut.isNewPasswordRequested(), false); 0274 QCOMPARE(uut.isExistingPasswordRequested(), true); 0275 QCOMPARE(uut.isPasswordAvailable(), false); 0276 QCOMPARE(uut.isKeyAvailable(), false); 0277 QCOMPARE(uut.key(), nullptr); 0278 const auto preservedChallenge = uut.challenge(); 0279 QVERIFY2(preservedChallenge, "should have the supplied password challenge by now"); 0280 QCOMPARE(preservedChallenge->cryptText(), m_challenge->cryptText()); 0281 QCOMPARE(preservedChallenge->nonce(), m_challenge->nonce()); 0282 0283 QCOMPARE(newPasswordNeeded.count(), 0); 0284 QCOMPARE(passwordAvailable.count(), 0); 0285 QCOMPARE(existingPasswordNeeded.count(), 1); 0286 QCOMPARE(keyAvailable.count(), 0); 0287 QCOMPARE(keyFailed.count(), 0); 0288 QCOMPARE(requestsCancelled.count(), 0); 0289 0290 // advance the state: cancel the request 0291 uut.cancelRequests(); 0292 QVERIFY2(test::signal_eventually_emitted_once(requestsCancelled), "requests for (new) password should be cancelled by now"); 0293 0294 // check the state is correctly updated 0295 QCOMPARE(uut.isStillAlive(), false); 0296 QCOMPARE(uut.isNewPasswordRequested(), false); 0297 QCOMPARE(uut.isExistingPasswordRequested(), true); 0298 QCOMPARE(uut.isPasswordAvailable(), false); 0299 QCOMPARE(uut.isKeyAvailable(), false); 0300 QCOMPARE(uut.key(), nullptr); 0301 QVERIFY2(!uut.challenge(), "should no longer acknowledge to the supplied password challenge"); 0302 0303 QCOMPARE(newPasswordNeeded.count(), 0); 0304 QCOMPARE(passwordAvailable.count(), 0); 0305 QCOMPARE(existingPasswordNeeded.count(), 1); 0306 QCOMPARE(keyAvailable.count(), 0); 0307 QCOMPARE(keyFailed.count(), 0); 0308 QCOMPARE(requestsCancelled.count(), 1); 0309 } 0310 0311 void PasswordFlowTest::supplyExistingPassword(void) 0312 { 0313 accounts::AccountSecret uut; 0314 QSignalSpy existingPasswordNeeded(&uut, &accounts::AccountSecret::existingPasswordNeeded); 0315 QSignalSpy newPasswordNeeded(&uut, &accounts::AccountSecret::newPasswordNeeded); 0316 QSignalSpy passwordAvailable(&uut, &accounts::AccountSecret::passwordAvailable); 0317 QSignalSpy requestsCancelled(&uut, &accounts::AccountSecret::requestsCancelled); 0318 QSignalSpy keyAvailable(&uut, &accounts::AccountSecret::keyAvailable); 0319 QSignalSpy keyFailed(&uut, &accounts::AccountSecret::keyFailed); 0320 0321 // check correct initial state is reported 0322 QCOMPARE(uut.isStillAlive(), true); 0323 QCOMPARE(uut.isNewPasswordRequested(), false); 0324 QCOMPARE(uut.isExistingPasswordRequested(), false); 0325 QCOMPARE(uut.isPasswordAvailable(), false); 0326 QCOMPARE(uut.isKeyAvailable(), false); 0327 QCOMPARE(uut.key(), nullptr); 0328 QVERIFY2(!uut.challenge(), "should not have a password challenge yet"); 0329 0330 // advance the state: request password 0331 QVERIFY2(uut.requestExistingPassword(*m_challenge, m_salt, *keyParams), "should be able to request a (existing) password"); 0332 QVERIFY2(test::signal_eventually_emitted_once(existingPasswordNeeded), "request for (existing) password should be signalled"); 0333 const auto suppliedChallenge = uut.challenge(); 0334 QVERIFY2(suppliedChallenge, "should have the supplied password challenge by now"); 0335 QCOMPARE(suppliedChallenge->cryptText(), m_challenge->cryptText()); 0336 QCOMPARE(suppliedChallenge->nonce(), m_challenge->nonce()); 0337 0338 // check the state is correctly updated 0339 QCOMPARE(uut.isStillAlive(), true); 0340 QCOMPARE(uut.isNewPasswordRequested(), false); 0341 QCOMPARE(uut.isExistingPasswordRequested(), true); 0342 QCOMPARE(uut.isPasswordAvailable(), false); 0343 QCOMPARE(uut.isKeyAvailable(), false); 0344 QCOMPARE(uut.key(), nullptr); 0345 0346 QCOMPARE(newPasswordNeeded.count(), 0); 0347 QCOMPARE(passwordAvailable.count(), 0); 0348 QCOMPARE(existingPasswordNeeded.count(), 1); 0349 QCOMPARE(keyAvailable.count(), 0); 0350 QCOMPARE(keyFailed.count(), 0); 0351 QCOMPARE(requestsCancelled.count(), 0); 0352 0353 // advance the state: supply password 0354 QString password = QString::fromUtf8(masterPassword()); 0355 QString wiped = QStringLiteral("*").repeated(password.size()); 0356 0357 QVERIFY2(uut.answerExistingPassword(password), "(existing) password should be accepted"); 0358 QVERIFY2(test::signal_eventually_emitted_once(passwordAvailable), "availability of the (existing) password should be signalled"); 0359 QCOMPARE(password, wiped); 0360 0361 // check the state is correctly updated 0362 QCOMPARE(uut.isStillAlive(), true); 0363 QCOMPARE(uut.isNewPasswordRequested(), false); 0364 QCOMPARE(uut.isExistingPasswordRequested(), true); 0365 QCOMPARE(uut.isPasswordAvailable(), true); 0366 QCOMPARE(uut.isKeyAvailable(), false); 0367 QCOMPARE(uut.key(), nullptr); 0368 const auto preservedChallenge = uut.challenge(); 0369 QVERIFY2(preservedChallenge, "should still have the same supplied password challenge after answering with a password"); 0370 QCOMPARE(preservedChallenge->cryptText(), m_challenge->cryptText()); 0371 QCOMPARE(preservedChallenge->nonce(), m_challenge->nonce()); 0372 0373 QCOMPARE(newPasswordNeeded.count(), 0); 0374 QCOMPARE(passwordAvailable.count(), 1); 0375 QCOMPARE(existingPasswordNeeded.count(), 1); 0376 QCOMPARE(keyAvailable.count(), 0); 0377 QCOMPARE(keyFailed.count(), 0); 0378 QCOMPARE(requestsCancelled.count(), 0); 0379 0380 // advance the state: derive the master key 0381 QVERIFY2(uut.deriveKey(), "key derivation should succeed"); 0382 QVERIFY2(test::signal_eventually_emitted_once(keyAvailable), "availability of the master key should be signalled"); 0383 0384 // check the state is correctly updated 0385 QCOMPARE(uut.isStillAlive(), true); 0386 QCOMPARE(uut.isNewPasswordRequested(), false); 0387 QCOMPARE(uut.isExistingPasswordRequested(), true); 0388 QCOMPARE(uut.isPasswordAvailable(), false); 0389 QCOMPARE(uut.isKeyAvailable(), true); 0390 QVERIFY2(uut.key(), "should have a master key by now"); 0391 const auto finalChallenge = uut.challenge(); 0392 QVERIFY2(finalChallenge, "should still have the same supplied password challenge after key derivation"); 0393 QCOMPARE(finalChallenge->cryptText(), m_challenge->cryptText()); 0394 QCOMPARE(finalChallenge->nonce(), m_challenge->nonce()); 0395 0396 QCOMPARE(newPasswordNeeded.count(), 0); 0397 QCOMPARE(passwordAvailable.count(), 1); 0398 QCOMPARE(existingPasswordNeeded.count(), 1); 0399 QCOMPARE(keyAvailable.count(), 1); 0400 QCOMPARE(keyFailed.count(), 0); 0401 QCOMPARE(requestsCancelled.count(), 0); 0402 } 0403 0404 void PasswordFlowTest::retryExistingPassword(void) 0405 { 0406 accounts::AccountSecret uut; 0407 QSignalSpy existingPasswordNeeded(&uut, &accounts::AccountSecret::existingPasswordNeeded); 0408 QSignalSpy newPasswordNeeded(&uut, &accounts::AccountSecret::newPasswordNeeded); 0409 QSignalSpy passwordAvailable(&uut, &accounts::AccountSecret::passwordAvailable); 0410 QSignalSpy requestsCancelled(&uut, &accounts::AccountSecret::requestsCancelled); 0411 QSignalSpy keyAvailable(&uut, &accounts::AccountSecret::keyAvailable); 0412 QSignalSpy keyFailed(&uut, &accounts::AccountSecret::keyFailed); 0413 0414 // check correct initial state is reported 0415 QCOMPARE(uut.isStillAlive(), true); 0416 QCOMPARE(uut.isNewPasswordRequested(), false); 0417 QCOMPARE(uut.isExistingPasswordRequested(), false); 0418 QCOMPARE(uut.isPasswordAvailable(), false); 0419 QCOMPARE(uut.isKeyAvailable(), false); 0420 QCOMPARE(uut.key(), nullptr); 0421 QVERIFY2(!uut.challenge(), "should not have a password challenge yet"); 0422 0423 // advance the state: request password 0424 QVERIFY2(uut.requestExistingPassword(*m_challenge, m_salt, *keyParams), "should be able to request a (existing) password"); 0425 QVERIFY2(test::signal_eventually_emitted_once(existingPasswordNeeded), "request for (existing) password should be signalled"); 0426 const auto suppliedChallenge = uut.challenge(); 0427 QVERIFY2(suppliedChallenge, "should have the supplied password challenge by now"); 0428 QCOMPARE(suppliedChallenge->cryptText(), m_challenge->cryptText()); 0429 QCOMPARE(suppliedChallenge->nonce(), m_challenge->nonce()); 0430 0431 // check the state is correctly updated 0432 QCOMPARE(uut.isStillAlive(), true); 0433 QCOMPARE(uut.isNewPasswordRequested(), false); 0434 QCOMPARE(uut.isExistingPasswordRequested(), true); 0435 QCOMPARE(uut.isPasswordAvailable(), false); 0436 QCOMPARE(uut.isKeyAvailable(), false); 0437 QCOMPARE(uut.key(), nullptr); 0438 0439 QCOMPARE(newPasswordNeeded.count(), 0); 0440 QCOMPARE(passwordAvailable.count(), 0); 0441 QCOMPARE(existingPasswordNeeded.count(), 1); 0442 QCOMPARE(keyAvailable.count(), 0); 0443 QCOMPARE(keyFailed.count(), 0); 0444 QCOMPARE(requestsCancelled.count(), 0); 0445 0446 // advance the state: supply wrong password 0447 QString wrongPassword(QStringLiteral("wrong")); 0448 QString wipedWrongPassword = QStringLiteral("*").repeated(wrongPassword.size()); 0449 0450 QVERIFY2(uut.answerExistingPassword(wrongPassword), "password attempt should be accepted"); 0451 QVERIFY2(test::signal_eventually_emitted_once(passwordAvailable), "availability of an attempt should be signalled"); 0452 QCOMPARE(wrongPassword, wipedWrongPassword); 0453 0454 // advance the state: attempt to derive the master key 0455 QVERIFY2(!uut.deriveKey(), "key derivation should fail on wrong password"); 0456 QVERIFY2(test::signal_eventually_emitted_once(keyFailed), "failure to derive the master key should be signalled"); 0457 0458 // check the state is correctly updated 0459 QCOMPARE(uut.isStillAlive(), true); 0460 QCOMPARE(uut.isNewPasswordRequested(), false); 0461 QCOMPARE(uut.isExistingPasswordRequested(), true); 0462 QCOMPARE(uut.isPasswordAvailable(), false); 0463 QCOMPARE(uut.isKeyAvailable(), false); 0464 QCOMPARE(uut.key(), nullptr); 0465 const auto stillPreservedChallenge = uut.challenge(); 0466 QVERIFY2(stillPreservedChallenge, "should still have the same supplied password challenge after answering with a password"); 0467 QCOMPARE(stillPreservedChallenge->cryptText(), m_challenge->cryptText()); 0468 QCOMPARE(stillPreservedChallenge->nonce(), m_challenge->nonce()); 0469 0470 QCOMPARE(newPasswordNeeded.count(), 0); 0471 QCOMPARE(passwordAvailable.count(), 1); 0472 QCOMPARE(existingPasswordNeeded.count(), 1); 0473 QCOMPARE(keyAvailable.count(), 0); 0474 QCOMPARE(requestsCancelled.count(), 0); 0475 0476 // advance the state: supply correct password 0477 QString correctPassword = QString::fromUtf8(masterPassword()); 0478 QString wipedCorrectPassword = QStringLiteral("*").repeated(correctPassword.size()); 0479 0480 QVERIFY2(uut.answerExistingPassword(correctPassword), "(existing) password should be accepted"); 0481 QVERIFY2(test::signal_eventually_emitted_twice(passwordAvailable), "availability of the (existing) password should be signalled"); 0482 QCOMPARE(correctPassword, wipedCorrectPassword); 0483 0484 // advance the state: attempt to derive the master key 0485 QVERIFY2(uut.deriveKey(), "key derivation should succeed"); 0486 QVERIFY2(test::signal_eventually_emitted_once(keyAvailable), "availability of the master key should be signalled"); 0487 0488 // check the state is correctly updated 0489 QCOMPARE(uut.isStillAlive(), true); 0490 QCOMPARE(uut.isNewPasswordRequested(), false); 0491 QCOMPARE(uut.isExistingPasswordRequested(), true); 0492 QCOMPARE(uut.isPasswordAvailable(), false); 0493 QCOMPARE(uut.isKeyAvailable(), true); 0494 QVERIFY2(uut.key(), "should have a master key by now"); 0495 const auto finalChallenge = uut.challenge(); 0496 QVERIFY2(finalChallenge, "should still have the same supplied password challenge after key derivation"); 0497 QCOMPARE(finalChallenge->cryptText(), m_challenge->cryptText()); 0498 QCOMPARE(finalChallenge->nonce(), m_challenge->nonce()); 0499 0500 QCOMPARE(newPasswordNeeded.count(), 0); 0501 QCOMPARE(passwordAvailable.count(), 2); 0502 QCOMPARE(existingPasswordNeeded.count(), 1); 0503 QCOMPARE(keyAvailable.count(), 1); 0504 QCOMPARE(requestsCancelled.count(), 0); 0505 } 0506 0507 QTEST_MAIN(PasswordFlowTest) 0508 0509 #include "account-secret-password-flow.moc"