| 1 | /** |
| 2 | * Copyright (C) 2004-2006 Brad Hards <bradh@frogmouth.net> |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include <QTest> |
| 27 | #include <QtCrypto> |
| 28 | |
| 29 | #ifdef QT_STATICPLUGIN |
| 30 | #include "import_plugins.h" |
| 31 | #endif |
| 32 | |
| 33 | class KDFUnitTest : public QObject |
| 34 | { |
| 35 | Q_OBJECT |
| 36 | |
| 37 | private Q_SLOTS: |
| 38 | void initTestCase(); |
| 39 | void cleanupTestCase(); |
| 40 | void pbkdf1md2Tests_data(); |
| 41 | void pbkdf1md2Tests(); |
| 42 | void pbkdf1sha1Tests_data(); |
| 43 | void pbkdf1sha1Tests(); |
| 44 | void pbkdf1sha1TimeTest(); |
| 45 | void pbkdf2Tests_data(); |
| 46 | void pbkdf2Tests(); |
| 47 | void pbkdf2TimeTest(); |
| 48 | void pbkdf2extraTests(); |
| 49 | void hkdfTests_data(); |
| 50 | void hkdfTests(); |
| 51 | |
| 52 | private: |
| 53 | QCA::Initializer *m_init; |
| 54 | QStringList providersToTest; |
| 55 | }; |
| 56 | |
| 57 | void KDFUnitTest::initTestCase() |
| 58 | { |
| 59 | m_init = new QCA::Initializer; |
| 60 | |
| 61 | const auto providers = QCA::providers(); |
| 62 | for (QCA::Provider *provider : providers) |
| 63 | providersToTest << provider->name(); |
| 64 | } |
| 65 | |
| 66 | void KDFUnitTest::cleanupTestCase() |
| 67 | { |
| 68 | delete m_init; |
| 69 | } |
| 70 | |
| 71 | void KDFUnitTest::pbkdf1md2Tests_data() |
| 72 | { |
| 73 | QTest::addColumn<QString>(name: "secret" ); // usually a password or passphrase |
| 74 | QTest::addColumn<QString>(name: "output" ); // the key you get back |
| 75 | QTest::addColumn<QString>(name: "salt" ); // a salt or initialisation vector |
| 76 | QTest::addColumn<unsigned int>(name: "outputLength" ); // if the algo supports variable length keys, len |
| 77 | QTest::addColumn<unsigned int>(name: "iterationCount" ); // number of iterations |
| 78 | |
| 79 | // These are from Botan's test suite |
| 80 | QTest::newRow(dataTag: "1" ) << QStringLiteral("71616c7a73656774" ) << QStringLiteral("7c1991f3f38a09d70cf3b1acadb70bc6" ) |
| 81 | << QStringLiteral("40cf117c3865e0cf" ) << static_cast<unsigned int>(16) |
| 82 | << static_cast<unsigned int>(1000); |
| 83 | |
| 84 | QTest::newRow(dataTag: "2" ) << QStringLiteral("766e68617a6a66736978626f6d787175" ) |
| 85 | << QStringLiteral("677500eda9f0c5e96e0a11f90fb9" ) << QStringLiteral("3a2484ce5d3e1b4d" ) |
| 86 | << static_cast<unsigned int>(14) << static_cast<unsigned int>(1); |
| 87 | |
| 88 | QTest::newRow(dataTag: "3" ) << QStringLiteral("66686565746e657162646d7171716e797977696f716a666c6f6976636371756a" ) |
| 89 | << QStringLiteral("91a5b689156b441bf27dd2bdd276" ) |
| 90 | << QStringLiteral("5d838b0f4fa22bfa2157f9083d87f8752e0495bb2113012761ef11b66e87c3cb" ) |
| 91 | << static_cast<unsigned int>(14) << static_cast<unsigned int>(15); |
| 92 | |
| 93 | QTest::newRow(dataTag: "4" ) << QStringLiteral("736e6279696e6a7075696b7176787867726c6b66" ) |
| 94 | << QStringLiteral("49516935cc9f438bafa30ff038fb" ) |
| 95 | << QStringLiteral("f22d341361b47e3390107bd973fdc0d3e0bc02a3" ) << static_cast<unsigned int>(14) |
| 96 | << static_cast<unsigned int>(2); |
| 97 | } |
| 98 | |
| 99 | void KDFUnitTest::pbkdf1md2Tests() |
| 100 | { |
| 101 | QFETCH(QString, secret); |
| 102 | QFETCH(QString, output); |
| 103 | QFETCH(QString, salt); |
| 104 | QFETCH(unsigned int, outputLength); |
| 105 | QFETCH(unsigned int, iterationCount); |
| 106 | |
| 107 | bool anyProviderTested = false; |
| 108 | foreach (QString provider, providersToTest) { |
| 109 | if (QCA::isSupported(features: "pbkdf1(md2)" , provider)) { |
| 110 | anyProviderTested = true; |
| 111 | QCA::SecureArray password = QCA::hexToArray(hexString: secret); |
| 112 | QCA::InitializationVector iv(QCA::hexToArray(hexString: salt)); |
| 113 | QCA::SymmetricKey key = |
| 114 | QCA::PBKDF1(QStringLiteral("md2" ), provider).makeKey(secret: password, salt: iv, keyLength: outputLength, iterationCount); |
| 115 | QCOMPARE(QCA::arrayToHex(key.toByteArray()), output); |
| 116 | } |
| 117 | } |
| 118 | if (!anyProviderTested) |
| 119 | qWarning() << "NONE of the providers supports PBKDF version 1 with MD2:" << providersToTest; |
| 120 | } |
| 121 | |
| 122 | void KDFUnitTest::pbkdf1sha1Tests_data() |
| 123 | { |
| 124 | QTest::addColumn<QString>(name: "secret" ); // usually a password or passphrase |
| 125 | QTest::addColumn<QString>(name: "output" ); // the key you get back |
| 126 | QTest::addColumn<QString>(name: "salt" ); // a salt or initialisation vector |
| 127 | QTest::addColumn<unsigned int>(name: "outputLength" ); // if the algo supports variable length keys, len |
| 128 | QTest::addColumn<unsigned int>(name: "iterationCount" ); // number of iterations |
| 129 | |
| 130 | // These are from Botan's test suite |
| 131 | QTest::newRow(dataTag: "1" ) << QStringLiteral("66746c6b6662786474626a62766c6c7662776977" ) |
| 132 | << QStringLiteral("768b277dc970f912dbdd3edad48ad2f065d25d" ) |
| 133 | << QStringLiteral("40ac5837560251c275af5e30a6a3074e57ced38e" ) << static_cast<unsigned int>(19) |
| 134 | << static_cast<unsigned int>(6); |
| 135 | |
| 136 | QTest::newRow(dataTag: "2" ) << QStringLiteral("786e736f736d6b766867677a7370636e63706f63" ) |
| 137 | << QStringLiteral("4d90e846a4b6aaa02ac548014a00e97e506b2afb" ) |
| 138 | << QStringLiteral("7008a9dc1b9a81470a2360275c19dab77f716824" ) << static_cast<unsigned int>(20) |
| 139 | << static_cast<unsigned int>(6); |
| 140 | |
| 141 | QTest::newRow(dataTag: "3" ) << QStringLiteral("6f74696c71776c756b717473" ) |
| 142 | << QStringLiteral("71ed1a995e693efcd33155935e800037da74ea28" ) |
| 143 | << QStringLiteral("ccfc44c09339040e55d3f7f76ca6ef838fde928717241deb9ac1a4ef45a27711" ) |
| 144 | << static_cast<unsigned int>(20) << static_cast<unsigned int>(2001); |
| 145 | |
| 146 | QTest::newRow(dataTag: "4" ) << QStringLiteral("6b7a6e657166666c6274767374686e6663746166" ) |
| 147 | << QStringLiteral("f345fb8fbd880206b650266661f6" ) |
| 148 | << QStringLiteral("8108883fc04a01feb10661651516425dad1c93e0" ) << static_cast<unsigned int>(14) |
| 149 | << static_cast<unsigned int>(10000); |
| 150 | |
| 151 | QTest::newRow(dataTag: "5" ) << QStringLiteral("716b78686c7170656d7868796b6d7975636a626f" ) |
| 152 | << QStringLiteral("2d54dfed0c7ef7d20b0945ba414a" ) |
| 153 | << QStringLiteral("bc8bc53d4604977c3adb1d19c15e87b77a84c2f6" ) << static_cast<unsigned int>(14) |
| 154 | << static_cast<unsigned int>(10000); |
| 155 | } |
| 156 | |
| 157 | void KDFUnitTest::pbkdf1sha1Tests() |
| 158 | { |
| 159 | QFETCH(QString, secret); |
| 160 | QFETCH(QString, output); |
| 161 | QFETCH(QString, salt); |
| 162 | QFETCH(unsigned int, outputLength); |
| 163 | QFETCH(unsigned int, iterationCount); |
| 164 | |
| 165 | bool anyProviderTested = false; |
| 166 | foreach (QString provider, providersToTest) { |
| 167 | if (QCA::isSupported(features: "pbkdf1(sha1)" , provider)) { |
| 168 | anyProviderTested = true; |
| 169 | QCA::SecureArray password = QCA::hexToArray(hexString: secret); |
| 170 | QCA::InitializationVector iv(QCA::hexToArray(hexString: salt)); |
| 171 | QCA::PBKDF1 pbkdf = QCA::PBKDF1(QStringLiteral("sha1" ), provider); |
| 172 | QCA::PBKDF1 copy = pbkdf; |
| 173 | copy.context(); // detach |
| 174 | QCA::SymmetricKey key = pbkdf.makeKey(secret: password, salt: iv, keyLength: outputLength, iterationCount); |
| 175 | QCOMPARE(QCA::arrayToHex(key.toByteArray()), output); |
| 176 | } |
| 177 | } |
| 178 | if (!anyProviderTested) |
| 179 | qWarning() << "NONE of the providers supports PBKDF version 1 with SHA1:" << providersToTest; |
| 180 | } |
| 181 | |
| 182 | void KDFUnitTest::pbkdf1sha1TimeTest() |
| 183 | { |
| 184 | QCA::SecureArray password("secret" ); |
| 185 | QCA::InitializationVector iv(QByteArray("salt" )); |
| 186 | unsigned int outputLength = 20; |
| 187 | int timeInterval = 200; |
| 188 | unsigned int iterationCount; |
| 189 | |
| 190 | foreach (QString provider, providersToTest) { |
| 191 | if (QCA::isSupported(features: "pbkdf1(sha1)" , provider)) { |
| 192 | QCA::SymmetricKey key1(QCA::PBKDF1(QStringLiteral("sha1" ), provider) |
| 193 | .makeKey(secret: password, salt: iv, keyLength: outputLength, msecInterval: timeInterval, iterationCount: &iterationCount)); |
| 194 | |
| 195 | QCA::SymmetricKey key2( |
| 196 | QCA::PBKDF1(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt: iv, keyLength: outputLength, iterationCount)); |
| 197 | |
| 198 | QCOMPARE(key1, key2); |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | void KDFUnitTest::pbkdf2Tests_data() |
| 204 | { |
| 205 | QTest::addColumn<QString>(name: "secret" ); // usually a password or passphrase |
| 206 | QTest::addColumn<QString>(name: "output" ); // the key you get back |
| 207 | QTest::addColumn<QString>(name: "salt" ); // a salt or initialisation vector |
| 208 | QTest::addColumn<unsigned int>(name: "outputLength" ); // if the algo supports variable length keys, len |
| 209 | QTest::addColumn<unsigned int>(name: "iterationCount" ); // number of iterations |
| 210 | |
| 211 | // These are from Botan's test suite |
| 212 | QTest::newRow(dataTag: "1" ) << QStringLiteral("6a79756571677872736367676c707864796b6366" ) |
| 213 | << QStringLiteral("df6d9d72872404bf73e708cf3b7d" ) |
| 214 | << QStringLiteral("9b56e55328a4c97a250738f8dba1b992e8a1b508" ) << static_cast<unsigned int>(14) |
| 215 | << static_cast<unsigned int>(10000); |
| 216 | |
| 217 | QTest::newRow(dataTag: "2" ) << QStringLiteral("61717271737a6e7a76767a67746b73616d6d676f" ) |
| 218 | << QStringLiteral( |
| 219 | "fa13f40af1ade2a30f2fffd66fc8a659ef95e6388c1682fc0fe4d15a70109517a32942e39c371440" ) |
| 220 | << QStringLiteral("57487813cdd2220dfc485d932a2979ee8769ea8b" ) << static_cast<unsigned int>(40) |
| 221 | << static_cast<unsigned int>(101); |
| 222 | |
| 223 | QTest::newRow(dataTag: "3" ) << QStringLiteral("6c7465786d666579796c6d6c62727379696b6177" ) |
| 224 | << QStringLiteral("027afadd48f4be8dcc4f" ) |
| 225 | << QStringLiteral("ed1f39a0a7f3889aaf7e60743b3bc1cc2c738e60" ) << static_cast<unsigned int>(10) |
| 226 | << static_cast<unsigned int>(1000); |
| 227 | |
| 228 | QTest::newRow(dataTag: "4" ) << QStringLiteral("6378676e7972636772766c6c796c6f6c736a706f" ) |
| 229 | << QStringLiteral("7c0d009fc91b48cb6d19bafbfccff3e2ccabfe725eaa234e56bde1d551c132f2" ) |
| 230 | << QStringLiteral("94ac88200743fb0f6ac51be62166cbef08d94c15" ) << static_cast<unsigned int>(32) |
| 231 | << static_cast<unsigned int>(1); |
| 232 | |
| 233 | QTest::newRow(dataTag: "5" ) << QStringLiteral("7871796668727865686965646c6865776e76626a" ) |
| 234 | << QStringLiteral("4661301d3517ca4443a6a607b32b2a63f69996299df75db75f1e0b98dd0eb7d8" ) |
| 235 | << QStringLiteral("24a1a50b17d63ee8394b69fc70887f4f94883d68" ) << static_cast<unsigned int>(32) |
| 236 | << static_cast<unsigned int>(5); |
| 237 | |
| 238 | QTest::newRow(dataTag: "6" ) << QStringLiteral("616e6461716b706a7761627663666e706e6a6b6c" ) |
| 239 | << QStringLiteral("82fb44a521448d5aac94b5158ead1e4dcd7363081a747b9f7626752bda2d" ) |
| 240 | << QStringLiteral("9316c80801623cc2734af74bec42cf4dbaa3f6d5" ) << static_cast<unsigned int>(30) |
| 241 | << static_cast<unsigned int>(100); |
| 242 | |
| 243 | QTest::newRow(dataTag: "7" ) << QStringLiteral("687361767679766f636c6f79757a746c736e6975" ) |
| 244 | << QStringLiteral("f8ec2b0ac817896ac8189d787c6424ed24a6d881436687a4629802c0ecce" ) |
| 245 | << QStringLiteral("612cc61df3cf2bdb36e10c4d8c9d73192bddee05" ) << static_cast<unsigned int>(30) |
| 246 | << static_cast<unsigned int>(100); |
| 247 | |
| 248 | QTest::newRow(dataTag: "8" ) << QStringLiteral("6561696d72627a70636f706275736171746b6d77" ) |
| 249 | << QStringLiteral("c9a0b2622f13916036e29e7462e206e8ba5b50ce9212752eb8ea2a4aa7b40a4cc1bf" ) |
| 250 | << QStringLiteral("45248f9d0cebcb86a18243e76c972a1f3b36772a" ) << static_cast<unsigned int>(34) |
| 251 | << static_cast<unsigned int>(100); |
| 252 | |
| 253 | QTest::newRow(dataTag: "9" ) << QStringLiteral("67777278707178756d7364736d626d6866686d666463766c63766e677a6b6967" ) |
| 254 | << QStringLiteral( |
| 255 | "4c9db7ba24955225d5b845f65ef24ef1b0c6e86f2e39c8ddaa4b8abd26082d1f350381fadeaeb560dc447afc" |
| 256 | "68a6b47e6ea1e7412f6cf7b2d82342fccd11d3b4" ) |
| 257 | << QStringLiteral("a39b76c6eec8374a11493ad08c246a3e40dfae5064f4ee3489c273646178" ) |
| 258 | << static_cast<unsigned int>(64) << static_cast<unsigned int>(1000); |
| 259 | } |
| 260 | |
| 261 | void KDFUnitTest::pbkdf2Tests() |
| 262 | { |
| 263 | QFETCH(QString, secret); |
| 264 | QFETCH(QString, output); |
| 265 | QFETCH(QString, salt); |
| 266 | QFETCH(unsigned int, outputLength); |
| 267 | QFETCH(unsigned int, iterationCount); |
| 268 | |
| 269 | bool anyProviderTested = false; |
| 270 | foreach (QString provider, providersToTest) { |
| 271 | if (QCA::isSupported(features: "pbkdf2(sha1)" , provider)) { |
| 272 | anyProviderTested = true; |
| 273 | QCA::SecureArray password = QCA::hexToArray(hexString: secret); |
| 274 | QCA::InitializationVector iv(QCA::hexToArray(hexString: salt)); |
| 275 | QCA::PBKDF2 pbkdf = QCA::PBKDF2(QStringLiteral("sha1" ), provider); |
| 276 | QCA::PBKDF2 copy = pbkdf; |
| 277 | copy.context(); // detach |
| 278 | QCA::SymmetricKey key = pbkdf.makeKey(secret: password, salt: iv, keyLength: outputLength, iterationCount); |
| 279 | QCOMPARE(QCA::arrayToHex(key.toByteArray()), output); |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | if (!anyProviderTested) |
| 284 | qWarning() << "NONE of the providers supports PBKDF version 2 with SHA1:" << providersToTest; |
| 285 | } |
| 286 | |
| 287 | void KDFUnitTest::pbkdf2TimeTest() |
| 288 | { |
| 289 | QCA::SecureArray password("secret" ); |
| 290 | QCA::InitializationVector iv(QByteArray("salt" )); |
| 291 | unsigned int outputLength = 20; |
| 292 | int timeInterval = 200; |
| 293 | unsigned int iterationCount; |
| 294 | |
| 295 | foreach (QString provider, providersToTest) { |
| 296 | if (QCA::isSupported(features: "pbkdf2(sha1)" , provider)) { |
| 297 | QCA::SymmetricKey key1(QCA::PBKDF2(QStringLiteral("sha1" ), provider) |
| 298 | .makeKey(secret: password, salt: iv, keyLength: outputLength, msecInterval: timeInterval, iterationCount: &iterationCount)); |
| 299 | |
| 300 | QCA::SymmetricKey key2( |
| 301 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt: iv, keyLength: outputLength, iterationCount)); |
| 302 | |
| 303 | QCOMPARE(key1, key2); |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | void KDFUnitTest::() |
| 309 | { |
| 310 | foreach (QString provider, providersToTest) { |
| 311 | if (QCA::isSupported(features: "pbkdf2(sha1)" , provider)) { |
| 312 | // Not sure where this one came from... |
| 313 | { |
| 314 | QCA::InitializationVector salt(QCA::SecureArray("what do ya want for nothing?" )); |
| 315 | QCA::SecureArray password("Jefe" ); |
| 316 | int iterations = 1000; |
| 317 | QCA::SymmetricKey passwordOut = |
| 318 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 319 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 320 | QStringLiteral("6349e09cb6b8c1485cfa9780ee3264df" )); |
| 321 | } |
| 322 | |
| 323 | // RFC3962, Appendix B |
| 324 | { |
| 325 | QCA::InitializationVector salt(QCA::SecureArray("ATHENA.MIT.EDUraeburn" )); |
| 326 | QCA::SecureArray password("password" ); |
| 327 | int iterations = 1; |
| 328 | QCA::SymmetricKey passwordOut = |
| 329 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 330 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 331 | QStringLiteral("cdedb5281bb2f801565a1122b2563515" )); |
| 332 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 333 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 334 | QStringLiteral("cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837" )); |
| 335 | } |
| 336 | |
| 337 | // RFC3962, Appendix B |
| 338 | { |
| 339 | QCA::InitializationVector salt(QCA::SecureArray("ATHENA.MIT.EDUraeburn" )); |
| 340 | QCA::SecureArray password("password" ); |
| 341 | int iterations = 2; |
| 342 | QCA::SymmetricKey passwordOut = |
| 343 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 344 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 345 | QStringLiteral("01dbee7f4a9e243e988b62c73cda935d" )); |
| 346 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 347 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 348 | QStringLiteral("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86" )); |
| 349 | } |
| 350 | |
| 351 | // RFC3962, Appendix B |
| 352 | { |
| 353 | QCA::InitializationVector salt(QCA::SecureArray("ATHENA.MIT.EDUraeburn" )); |
| 354 | QCA::SecureArray password("password" ); |
| 355 | int iterations = 1200; |
| 356 | QCA::SymmetricKey passwordOut = |
| 357 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 358 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 359 | QStringLiteral("5c08eb61fdf71e4e4ec3cf6ba1f5512b" )); |
| 360 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 361 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 362 | QStringLiteral("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13" )); |
| 363 | } |
| 364 | |
| 365 | // RFC3211 and RFC3962, Appendix B |
| 366 | { |
| 367 | QCA::InitializationVector salt(QCA::hexToArray(QStringLiteral("1234567878563412" ))); |
| 368 | QCA::SecureArray password("password" ); |
| 369 | int iterations = 5; |
| 370 | QCA::SymmetricKey passwordOut = |
| 371 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 372 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 373 | QStringLiteral("d1daa78615f287e6a1c8b120d7062a49" )); |
| 374 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 375 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 376 | QStringLiteral("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee" )); |
| 377 | passwordOut = QCA::PBKDF2().makeKey(secret: password, salt, keyLength: 8, iterationCount: iterations); |
| 378 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), QStringLiteral("d1daa78615f287e6" )); |
| 379 | } |
| 380 | |
| 381 | // RFC3962, Appendix B |
| 382 | { |
| 383 | QCA::InitializationVector salt(QCA::SecureArray("pass phrase equals block size" )); |
| 384 | QCA::SecureArray password("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ); |
| 385 | int iterations = 1200; |
| 386 | QCA::SymmetricKey passwordOut = |
| 387 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 388 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 389 | QStringLiteral("139c30c0966bc32ba55fdbf212530ac9" )); |
| 390 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 391 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 392 | QStringLiteral("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1" )); |
| 393 | } |
| 394 | |
| 395 | // RFC3962, Appendix B |
| 396 | { |
| 397 | try { |
| 398 | QCA::InitializationVector salt(QCA::SecureArray("pass phrase exceeds block size" )); |
| 399 | QCA::SecureArray password("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ); |
| 400 | int iterations = 1200; |
| 401 | QCA::SymmetricKey passwordOut = |
| 402 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 403 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 404 | QStringLiteral("9ccad6d468770cd51b10e6a68721be61" )); |
| 405 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 406 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 407 | QStringLiteral("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a" )); |
| 408 | } catch (std::exception &) { |
| 409 | if (provider == QLatin1String("qca-botan" )) |
| 410 | qDebug() << "You should use a later version of Botan" ; |
| 411 | else |
| 412 | QFAIL("exception" ); |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // RFC3962, Appendix B |
| 417 | { |
| 418 | QCA::InitializationVector salt(QCA::SecureArray("EXAMPLE.COMpianist" )); |
| 419 | QCA::SecureArray password(QCA::hexToArray(QStringLiteral("f09d849e" ))); |
| 420 | int iterations = 50; |
| 421 | QCA::SymmetricKey passwordOut = |
| 422 | QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 16, iterationCount: iterations); |
| 423 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 424 | QStringLiteral("6b9cf26d45455a43a5b8bb276a403b39" )); |
| 425 | passwordOut = QCA::PBKDF2(QStringLiteral("sha1" ), provider).makeKey(secret: password, salt, keyLength: 32, iterationCount: iterations); |
| 426 | QCOMPARE(QCA::arrayToHex(passwordOut.toByteArray()), |
| 427 | QStringLiteral("6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52" )); |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | void KDFUnitTest::hkdfTests_data() |
| 434 | { |
| 435 | QTest::addColumn<QString>(name: "secret" ); // usually a password or passphrase |
| 436 | QTest::addColumn<QString>(name: "salt" ); // a salt or initialisation vector |
| 437 | QTest::addColumn<QString>(name: "info" ); // an additional info |
| 438 | QTest::addColumn<QString>(name: "output" ); // the key you get back |
| 439 | |
| 440 | // RFC 5869, Appendix A |
| 441 | QTest::newRow(dataTag: "1" ) << QStringLiteral("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" ) |
| 442 | << QStringLiteral("000102030405060708090a0b0c" ) << QStringLiteral("f0f1f2f3f4f5f6f7f8f9" ) |
| 443 | << QStringLiteral( |
| 444 | "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" ); |
| 445 | |
| 446 | QTest::newRow(dataTag: "2" ) << QStringLiteral( |
| 447 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b" |
| 448 | "2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f" ) |
| 449 | << QStringLiteral( |
| 450 | "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b" |
| 451 | "8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf" ) |
| 452 | << QStringLiteral( |
| 453 | "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadb" |
| 454 | "dcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" ) |
| 455 | << QStringLiteral( |
| 456 | "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c6" |
| 457 | "5e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87" ); |
| 458 | |
| 459 | QTest::newRow(dataTag: "3" ) << QStringLiteral("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" ) << QString() << QString() |
| 460 | << QStringLiteral( |
| 461 | "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8" ); |
| 462 | } |
| 463 | |
| 464 | void KDFUnitTest::hkdfTests() |
| 465 | { |
| 466 | QFETCH(QString, secret); |
| 467 | QFETCH(QString, salt); |
| 468 | QFETCH(QString, info); |
| 469 | QFETCH(QString, output); |
| 470 | |
| 471 | bool anyProviderTested = false; |
| 472 | foreach (QString provider, providersToTest) { |
| 473 | if (QCA::isSupported(features: "hkdf(sha256)" , provider)) { |
| 474 | anyProviderTested = true; |
| 475 | QCA::SecureArray password = QCA::hexToArray(hexString: secret); |
| 476 | QCA::InitializationVector saltv(QCA::hexToArray(hexString: salt)); |
| 477 | QCA::InitializationVector infov(QCA::hexToArray(hexString: info)); |
| 478 | QCA::HKDF hkdf = QCA::HKDF(QStringLiteral("sha256" ), provider); |
| 479 | QCA::HKDF copy = hkdf; |
| 480 | copy.context(); // detach |
| 481 | |
| 482 | QCA::SymmetricKey key = hkdf.makeKey(secret: password, salt: saltv, info: infov, keyLength: output.size() / 2); |
| 483 | QCOMPARE(QCA::arrayToHex(key.toByteArray()), output); |
| 484 | } |
| 485 | } |
| 486 | if (!anyProviderTested) |
| 487 | qWarning() << "NONE of the providers supports HKDF version 1 with SHA256:" << providersToTest; |
| 488 | } |
| 489 | |
| 490 | QTEST_MAIN(KDFUnitTest) |
| 491 | |
| 492 | #include "kdfunittest.moc" |
| 493 | |