diff options
Diffstat (limited to 'tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp')
-rw-r--r-- | tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp | 332 |
1 files changed, 292 insertions, 40 deletions
diff --git a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp index ca4a6f7354..c08afd67c4 100644 --- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp +++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp @@ -1,37 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QTest> +#include <QScopeGuard> #include <QCryptographicHash> #include <QtCore/QMetaEnum> +#include <thread> + Q_DECLARE_METATYPE(QCryptographicHash::Algorithm) class tst_QCryptographicHash : public QObject @@ -45,11 +23,25 @@ private slots: void sha1(); void sha3_data(); void sha3(); + void keccak(); + void keccak_data(); void blake2_data(); void blake2(); void files_data(); void files(); + void hashLength_data(); void hashLength(); + void addDataAcceptsNullByteArrayView_data() { hashLength_data(); } + void addDataAcceptsNullByteArrayView(); + void move(); + void swap(); + // keep last + void moreThan4GiBOfData_data(); + void moreThan4GiBOfData(); + void keccakBufferOverflow(); +private: + void ensureLargeData(); + std::vector<char> large; }; void tst_QCryptographicHash::repeated_result_data() @@ -63,19 +55,23 @@ void tst_QCryptographicHash::repeated_result() QCryptographicHash::Algorithm _algo = QCryptographicHash::Algorithm(algo); QCryptographicHash hash(_algo); + QCOMPARE_EQ(hash.algorithm(), _algo); + QFETCH(QByteArray, first); hash.addData(first); QFETCH(QByteArray, hash_first); - QByteArray result = hash.result(); + QByteArrayView result = hash.resultView(); QCOMPARE(result, hash_first); + QCOMPARE(result, hash.resultView()); QCOMPARE(result, hash.result()); hash.reset(); hash.addData(first); - result = hash.result(); + result = hash.resultView(); QCOMPARE(result, hash_first); QCOMPARE(result, hash.result()); + QCOMPARE(result, hash.resultView()); } void tst_QCryptographicHash::intermediary_result_data() @@ -156,6 +152,27 @@ void tst_QCryptographicHash::intermediary_result_data() << QByteArray("abc") << QByteArray("abc") << QByteArray::fromHex("B751850B1A57168A5693CD924B6B096E08F621827444F70D884F5D0240D2712E10E116E9192AF3C91A7EC57647E3934057340B4CF408D5A56592F8274EEC53F0") << QByteArray::fromHex("BB582DA40D15399ACF62AFCBBD6CFC9EE1DD5129B1EF9935DD3B21668F1A73D7841018BE3B13F281C3A8E9DA7EDB60F57B9F9F1C04033DF4CE3654B7B2ADB310"); + + QTest::newRow("keccak_224_abc_abc") + << int(QCryptographicHash::Keccak_224) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8") + << QByteArray::fromHex("048330e7c7c8b4a41ab713b3a6f958d77b8cf3ee969930f1584dd550"); + QTest::newRow("keccak_256_abc_abc") + << int(QCryptographicHash::Keccak_256) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + << QByteArray::fromHex("9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21"); + QTest::newRow("keccak_384_abc_abc") + << int(QCryptographicHash::Keccak_384) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e") + << QByteArray::fromHex("d733b87d392d270889d3da23ae113f349e25574b445f319cde4cd3f877c753e9e3c65980421339b3a131457ff393939f"); + QTest::newRow("keccak_512_abc_abc") + << int(QCryptographicHash::Keccak_512) + << QByteArray("abc") << QByteArray("abc") + << QByteArray::fromHex("18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96") + << QByteArray::fromHex("a7c392d2a42155761ca76bddde1c47d55486b007edf465397bfb9dfa74d11c8f0d7c86cd29415283f1b5e7f655cec25b869c9e9c33a8986f0b38542fb12bfb93"); } void tst_QCryptographicHash::intermediary_result() @@ -168,16 +185,14 @@ void tst_QCryptographicHash::intermediary_result() hash.addData(first); QFETCH(QByteArray, hash_first); - QByteArray result = hash.result(); - QCOMPARE(result, hash_first); + QCOMPARE(hash.resultView(), hash_first); // don't reset QFETCH(QByteArray, second); QFETCH(QByteArray, hash_firstsecond); hash.addData(second); - result = hash.result(); - QCOMPARE(result, hash_firstsecond); + QCOMPARE(hash.resultView(), hash_firstsecond); hash.reset(); } @@ -198,10 +213,7 @@ void tst_QCryptographicHash::sha1() // SHA1(A million repetitions of "a") = // 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F - QByteArray as; - for (int i = 0; i < 1000000; ++i) - as += 'a'; - QCOMPARE(QCryptographicHash::hash(as, QCryptographicHash::Sha1).toHex().toUpper(), + QCOMPARE(QCryptographicHash::hash(QByteArray(1'000'000, 'a'), QCryptographicHash::Sha1).toHex().toUpper(), QByteArray("34AA973CD4C4DAA4F61EEB2BDBAD27316534016F")); } @@ -267,6 +279,68 @@ void tst_QCryptographicHash::sha3() QCOMPARE(result, expectedResult); } +void tst_QCryptographicHash::keccak_data() +{ + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QByteArray>("expectedResult"); + +#define ROW(Tag, Algorithm, Input, Result) \ + QTest::newRow(Tag) << Algorithm << QByteArrayLiteral(Input) << QByteArray::fromHex(Result) + + ROW("keccak_224_pangram", + QCryptographicHash::Keccak_224, + "The quick brown fox jumps over the lazy dog", + "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe"); + + ROW("keccak_224_pangram_dot", + QCryptographicHash::Keccak_224, + "The quick brown fox jumps over the lazy dog.", + "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab"); + + ROW("keccak_256_pangram", + QCryptographicHash::Keccak_256, + "The quick brown fox jumps over the lazy dog", + "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"); + + ROW("keccak_256_pangram_dot", + QCryptographicHash::Keccak_256, + "The quick brown fox jumps over the lazy dog.", + "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d"); + + ROW("keccak_384_pangram", + QCryptographicHash::Keccak_384, + "The quick brown fox jumps over the lazy dog", + "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3"); + + ROW("keccak_384_pangram_dot", + QCryptographicHash::Keccak_384, + "The quick brown fox jumps over the lazy dog.", + "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b"); + + ROW("skeccak_512_pangram", + QCryptographicHash::Keccak_512, + "The quick brown fox jumps over the lazy dog", + "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609"); + + ROW("keccak_512_pangram_dot", + QCryptographicHash::Keccak_512, + "The quick brown fox jumps over the lazy dog.", + "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760"); + +#undef ROW +} + +void tst_QCryptographicHash::keccak() +{ + QFETCH(QCryptographicHash::Algorithm, algorithm); + QFETCH(QByteArray, data); + QFETCH(QByteArray, expectedResult); + + const auto result = QCryptographicHash::hash(data, algorithm); + QCOMPARE(result, expectedResult); +} + void tst_QCryptographicHash::blake2_data() { QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); @@ -400,14 +474,192 @@ void tst_QCryptographicHash::files() } } -void tst_QCryptographicHash::hashLength() +void tst_QCryptographicHash::hashLength_data() { + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); auto metaEnum = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); for (int i = 0, value = metaEnum.value(i); value != -1; value = metaEnum.value(++i)) { auto algorithm = QCryptographicHash::Algorithm(value); - QByteArray output = QCryptographicHash::hash(QByteArrayLiteral("test"), algorithm); - QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length()); + QTest::addRow("%s", metaEnum.key(i)) << algorithm; + } +} + +void tst_QCryptographicHash::hashLength() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + qsizetype expectedSize; + if (algorithm == QCryptographicHash::NumAlgorithms) { + // It's UB to call ::hash() with NumAlgorithms, but hashLength() is + // fine and returns 0 for invalid values: + expectedSize = 0; + } else { + expectedSize = QCryptographicHash::hash("test", algorithm).size(); } + QCOMPARE(QCryptographicHash::hashLength(algorithm), expectedSize); +} + +void tst_QCryptographicHash::addDataAcceptsNullByteArrayView() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + if (!QCryptographicHash::supportsAlgorithm(algorithm)) + QSKIP("QCryptographicHash doesn't support this algorithm"); + + QCryptographicHash hash1(algorithm); + hash1.addData("meep"); + hash1.addData(QByteArrayView{}); // after other data + + QCryptographicHash hash2(algorithm); + hash2.addData(QByteArrayView{}); // before any other data + hash2.addData("meep"); + + const auto expected = QCryptographicHash::hash("meep", algorithm); + + QCOMPARE(hash1.resultView(), expected); + QCOMPARE(hash2.resultView(), expected); +} + +void tst_QCryptographicHash::move() +{ + QCryptographicHash hash1(QCryptographicHash::Sha1); + hash1.addData("a"); + + // move constructor + auto hash2(std::move(hash1)); + hash2.addData("b"); + + // move assign operator + QCryptographicHash hash3(QCryptographicHash::Sha256); + hash3.addData("no effect on the end result"); + hash3 = std::move(hash2); + hash3.addData("c"); + + QCOMPARE(hash3.resultView(), QByteArray::fromHex("A9993E364706816ABA3E25717850C26C9CD0D89D")); +} + +void tst_QCryptographicHash::swap() +{ + QCryptographicHash hash1(QCryptographicHash::Sha1); + QCryptographicHash hash2(QCryptographicHash::Sha256); + + hash1.addData("da"); + hash2.addData("te"); + + hash1.swap(hash2); + + hash2.addData("ta"); + hash1.addData("st"); + + QCOMPARE(hash2.result(), QCryptographicHash::hash("data", QCryptographicHash::Sha1)); + QCOMPARE(hash1.result(), QCryptographicHash::hash("test", QCryptographicHash::Sha256)); +} + +void tst_QCryptographicHash::ensureLargeData() +{ +#if QT_POINTER_SIZE > 4 + QElapsedTimer timer; + timer.start(); + const size_t GiB = 1024 * 1024 * 1024; + if (large.size() == 4 * GiB + 1) + return; + try { + large.resize(4 * GiB + 1, '\0'); + } catch (const std::bad_alloc &) { + QSKIP("Could not allocate 4GiB plus one byte of RAM."); + } + QCOMPARE(large.size(), 4 * GiB + 1); + large.back() = '\1'; + qDebug("created dataset in %lld ms", timer.elapsed()); +#endif +} + +void tst_QCryptographicHash::moreThan4GiBOfData_data() +{ +#if QT_POINTER_SIZE > 4 + if (ensureLargeData(); large.empty()) + return; + QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); + auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); + auto row = [me] (QCryptographicHash::Algorithm algo) { + QTest::addRow("%s", me.valueToKey(int(algo))) << algo; + }; + // these are reasonably fast (O(secs)) + row(QCryptographicHash::Md4); + row(QCryptographicHash::Md5); + row(QCryptographicHash::Sha1); + if (!qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci")) { + // This is important but so slow (O(minute)) that, on CI, it tends to time out. + // Retain it for manual runs, all the same, as most dev machines will be fast enough. + row(QCryptographicHash::Sha512); + } + // the rest is just too slow +#else + QSKIP("This test is 64-bit only."); +#endif +} + +void tst_QCryptographicHash::moreThan4GiBOfData() +{ + QFETCH(const QCryptographicHash::Algorithm, algorithm); + + using MaybeThread = std::thread; + + QElapsedTimer timer; + timer.start(); + const auto sg = qScopeGuard([&] { + qDebug() << algorithm << "test finished in" << timer.restart() << "ms"; + }); + + const auto view = QByteArrayView{large}; + const auto first = view.first(view.size() / 2); + const auto last = view.sliced(view.size() / 2); + + QByteArray single; + QByteArray chunked; + + auto t = MaybeThread{[&] { + QCryptographicHash h(algorithm); + h.addData(view); + single = h.result(); + }}; + { + QCryptographicHash h(algorithm); + h.addData(first); + h.addData(last); + chunked = h.result(); + } + t.join(); + + QCOMPARE(single, chunked); +} + +void tst_QCryptographicHash::keccakBufferOverflow() +{ +#if QT_POINTER_SIZE == 4 + QSKIP("This is a 64-bit-only test"); +#else + + if (ensureLargeData(); large.empty()) + return; + + QElapsedTimer timer; + timer.start(); + const auto sg = qScopeGuard([&] { + qDebug() << "test finished in" << timer.restart() << "ms"; + }); + + constexpr qsizetype magic = INT_MAX/4; + QCOMPARE_GE(large.size(), size_t(magic + 1)); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Keccak_224); + const auto first = QByteArrayView{large}.first(1); + const auto second = QByteArrayView{large}.sliced(1, magic); + hash.addData(first); + hash.addData(second); + (void)hash.resultView(); + QVERIFY(true); // didn't crash +#endif } QTEST_MAIN(tst_QCryptographicHash) |