From cd3dece750aa30b15091f211a72b6fcf67d49853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Sun, 31 Aug 2014 14:55:06 +0300 Subject: ssl: common key parser support for encrypted keys This adds the infrastructure for reading and writing encrypted private keys when using non-OpenSSL backends. Each platform must provide its cryptographic encrypt / decrypt functions. As WinRT already uses the common parser, this commit includes an implementation for that platform. Done-with: Andrew Knight Task-number: QTBUG-40688 Change-Id: I0d153425ce63601ff03b784a111e13962061025f Reviewed-by: Richard J. Moore --- src/network/ssl/qsslkey_openssl.cpp | 3 +- src/network/ssl/qsslkey_p.cpp | 52 ++++++++- src/network/ssl/qsslkey_p.h | 13 ++- src/network/ssl/qsslkey_qt.cpp | 78 +++++++++++-- src/network/ssl/qsslkey_winrt.cpp | 98 ++++++++++++++++ tests/auto/network/ssl/qsslkey/qsslkey.pro | 3 + .../ssl/qsslkey/rsa-with-passphrase-rc2.pem | 18 +++ tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp | 126 +++++++++++++++++++-- 8 files changed, 367 insertions(+), 24 deletions(-) create mode 100644 tests/auto/network/ssl/qsslkey/rsa-with-passphrase-rc2.pem diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp index 7e78ac0fee..6b0fa954eb 100644 --- a/src/network/ssl/qsslkey_openssl.cpp +++ b/src/network/ssl/qsslkey_openssl.cpp @@ -111,7 +111,8 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear) { - decodePem(pemFromDer(der), QByteArray(), deepClear); + QMap headers; + decodePem(pemFromDer(der, headers), QByteArray(), deepClear); } void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index 2b0dab9933..b051ec6874 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -63,6 +63,7 @@ #include #include +#include #include #ifndef QT_NO_DEBUG_STREAM #include @@ -130,7 +131,7 @@ QByteArray QSslKeyPrivate::pemFooter() const Returns a DER key formatted as PEM. */ -QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const +QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap &headers) const { QByteArray pem(der.toBase64()); @@ -144,7 +145,16 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const if (rem) pem.append('\n'); // ### - pem.prepend(pemHeader() + '\n'); + QByteArray extra; + if (!headers.isEmpty()) { + QMap::const_iterator it = headers.constEnd(); + do { + it--; + extra += it.key() + ": " + it.value() + '\n'; + } while (it != headers.constBegin()); + extra += '\n'; + } + pem.prepend(pemHeader() + '\n' + extra); pem.append(pemFooter() + '\n'); return pem; @@ -155,7 +165,7 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const Returns a PEM key formatted as DER. */ -QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const +QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap *headers) const { const QByteArray header = pemHeader(); const QByteArray footer = pemFooter(); @@ -169,6 +179,39 @@ QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); + if (der.contains("Proc-Type:")) { + // taken from QHttpNetworkReplyPrivate::parseHeader + const QByteArrayMatcher lf("\n"); + const QByteArrayMatcher colon(":"); + int i = 0; + while (i < der.count()) { + int j = colon.indexIn(der, i); // field-name + if (j == -1) + break; + const QByteArray field = der.mid(i, j - i).trimmed(); + j++; + // any number of LWS is allowed before and after the value + QByteArray value; + do { + i = lf.indexIn(der, j); + if (i == -1) + break; + if (!value.isEmpty()) + value += ' '; + // check if we have CRLF or only LF + bool hasCR = (i && der[i-1] == '\r'); + int length = i -(hasCR ? 1: 0) - j; + value += der.mid(j, length).trimmed(); + j = ++i; + } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t')); + if (i == -1) + break; // something is wrong + + headers->insert(field, value); + } + der = der.mid(i); + } + return QByteArray::fromBase64(der); // ignores newlines } @@ -337,7 +380,8 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const return QByteArray(); #ifndef QT_NO_OPENSSL - return d->derFromPem(toPem(passPhrase)); + QMap headers; + return d->derFromPem(toPem(passPhrase), &headers); #else return d->derData; #endif diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index 9c1476038a..d24606e6a6 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -91,8 +91,8 @@ public: bool deepClear = true); QByteArray pemHeader() const; QByteArray pemFooter() const; - QByteArray pemFromDer(const QByteArray &der) const; - QByteArray derFromPem(const QByteArray &pem) const; + QByteArray pemFromDer(const QByteArray &der, const QMap &headers) const; + QByteArray derFromPem(const QByteArray &pem, QMap *headers) const; int length() const; QByteArray toPem(const QByteArray &passPhrase) const; @@ -106,6 +106,15 @@ public: RSA *rsa; DSA *dsa; #else + enum Cipher { + DesCbc, + DesEde3Cbc, + Rc2Cbc + }; + + Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); + Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); + Qt::HANDLE opaque; QByteArray derData; int keyLength; diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp index feeb7d6f87..c14cf0250c 100644 --- a/src/network/ssl/qsslkey_qt.cpp +++ b/src/network/ssl/qsslkey_qt.cpp @@ -43,6 +43,8 @@ #include "qsslkey_p.h" #include "qasn1element_p.h" +#include + QT_USE_NAMESPACE static const quint8 bits_table[256] = { @@ -78,6 +80,31 @@ static int numberOfBits(const QByteArray &modulus) return bits; } +static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) +{ + QByteArray key; + QCryptographicHash hash(QCryptographicHash::Md5); + hash.addData(passPhrase); + hash.addData(iv); + switch (cipher) { + case QSslKeyPrivate::DesCbc: + key = hash.result().left(8); + break; + case QSslKeyPrivate::DesEde3Cbc: + key = hash.result(); + hash.reset(); + hash.addData(key); + hash.addData(passPhrase); + hash.addData(iv); + key += hash.result().left(8); + break; + case QSslKeyPrivate::Rc2Cbc: + key = hash.result(); + break; + } + return key; +} + void QSslKeyPrivate::clear(bool deep) { Q_UNUSED(deep); @@ -155,12 +182,32 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear) void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) { - if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { - Q_UNIMPLEMENTED(); - return; - } + QMap headers; + QByteArray data = derFromPem(pem, &headers); + if (headers.value("Proc-Type") == "4,ENCRYPTED") { + QList dekInfo = headers.value("DEK-Info").split(','); + if (dekInfo.size() != 2) { + clear(deepClear); + return; + } + + Cipher cipher; + if (dekInfo.first() == "DES-CBC") { + cipher = DesCbc; + } else if (dekInfo.first() == "DES-EDE3-CBC") { + cipher = DesEde3Cbc; + } else if (dekInfo.first() == "RC2-CBC") { + cipher = Rc2Cbc; + } else { + clear(deepClear); + return; + } - decodeDer(derFromPem(pem), deepClear); + const QByteArray iv = QByteArray::fromHex(dekInfo.last()); + const QByteArray key = deriveKey(cipher, passPhrase, iv); + data = decrypt(cipher, data, key, iv); + } + decodeDer(data, deepClear); } int QSslKeyPrivate::length() const @@ -170,12 +217,27 @@ int QSslKeyPrivate::length() const QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const { + QByteArray data; + QMap headers; + if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { - Q_UNIMPLEMENTED(); - return QByteArray(); + // ### use a cryptographically secure random number generator + QByteArray iv; + iv.resize(8); + for (int i = 0; i < iv.size(); ++i) + iv[i] = (qrand() & 0xff); + + Cipher cipher = DesEde3Cbc; + const QByteArray key = deriveKey(cipher, passPhrase, iv); + data = encrypt(cipher, derData, key, iv); + + headers.insert("Proc-Type", "4,ENCRYPTED"); + headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex()); + } else { + data = derData; } - return pemFromDer(derData); + return pemFromDer(data, headers); } Qt::HANDLE QSslKeyPrivate::handle() const diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp index 2c83069694..c5b4146ee9 100644 --- a/src/network/ssl/qsslkey_winrt.cpp +++ b/src/network/ssl/qsslkey_winrt.cpp @@ -61,3 +61,101 @@ using namespace ABI::Windows::Security::Cryptography::Core; using namespace ABI::Windows::Storage::Streams; QT_USE_NAMESPACE + +struct SslKeyGlobal +{ + SslKeyGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_CryptographicEngine).Get(), + &engine); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr keyProviderFactory; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_SymmetricKeyAlgorithmProvider).Get(), + &keyProviderFactory); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"DES_CBC").Get(), + &keyProviders[QSslKeyPrivate::DesCbc]); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"3DES_CBC").Get(), + &keyProviders[QSslKeyPrivate::DesEde3Cbc]); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"RC2_CBC").Get(), + &keyProviders[QSslKeyPrivate::Rc2Cbc]); + Q_ASSERT_SUCCEEDED(hr); + + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr engine; + QHash> keyProviders; + ComPtr bufferFactory; +}; +Q_GLOBAL_STATIC(SslKeyGlobal, g) + +static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, QByteArray data, const QByteArray &key, const QByteArray &iv, bool encrypt) +{ + HRESULT hr; + + ISymmetricKeyAlgorithmProvider *keyProvider = g->keyProviders[cipher].Get(); + Q_ASSERT(keyProvider); + + ComPtr keyBuffer; + hr = g->bufferFactory->CreateFromByteArray(key.length(), (BYTE *)key.data(), &keyBuffer); + Q_ASSERT_SUCCEEDED(hr); + ComPtr cryptographicKey; + hr = keyProvider->CreateSymmetricKey(keyBuffer.Get(), &cryptographicKey); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 blockLength; + hr = keyProvider->get_BlockLength(&blockLength); + Q_ASSERT_SUCCEEDED(hr); + if (encrypt) { // Add padding + const char padding = blockLength - data.length() % blockLength; + data += QByteArray(padding, padding); + } + + ComPtr dataBuffer; + hr = g->bufferFactory->CreateFromByteArray(data.length(), (BYTE *)data.data(), &dataBuffer); + Q_ASSERT_SUCCEEDED(hr); + ComPtr ivBuffer; + hr = g->bufferFactory->CreateFromByteArray(iv.length(), (BYTE *)iv.data(), &ivBuffer); + Q_ASSERT_SUCCEEDED(hr); + ComPtr resultBuffer; + hr = encrypt ? g->engine->Encrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer) + : g->engine->Decrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 resultLength; + hr = resultBuffer->get_Length(&resultLength); + Q_ASSERT_SUCCEEDED(hr); + ComPtr bufferAccess; + hr = resultBuffer.As(&bufferAccess); + Q_ASSERT_SUCCEEDED(hr); + byte *resultData; + hr = bufferAccess->Buffer(&resultData); + Q_ASSERT_SUCCEEDED(hr); + + if (!encrypt) { // Remove padding + const uchar padding = resultData[resultLength - 1]; + if (padding > 0 && padding <= blockLength) + resultLength -= padding; + else + qWarning("Invalid padding length of %u; decryption likely failed.", padding); + } + + return QByteArray(reinterpret_cast(resultData), resultLength); +} + +QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) +{ + return doCrypt(cipher, data, key, iv, false); +} + +QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) +{ + return doCrypt(cipher, data, key, iv, true); +} diff --git a/tests/auto/network/ssl/qsslkey/qsslkey.pro b/tests/auto/network/ssl/qsslkey/qsslkey.pro index 78cfb9ce92..4ec4f27e6f 100644 --- a/tests/auto/network/ssl/qsslkey/qsslkey.pro +++ b/tests/auto/network/ssl/qsslkey/qsslkey.pro @@ -4,6 +4,9 @@ CONFIG += parallel_test SOURCES += tst_qsslkey.cpp !wince*:win32:LIBS += -lws2_32 QT = core network testlib +contains(QT_CONFIG, private_tests) { + QT += core-private network-private +} TARGET = tst_qsslkey diff --git a/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-rc2.pem b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-rc2.pem new file mode 100644 index 0000000000..7a0722fb8d --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-rc2.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: RC2-CBC,EAAF396E2CDD3680 + +G+kY27Vq0Bkw6dEGlpe4oyyXlilyKJgkX53NQHv0WtLsu+cfamGst9viSQbAluP0 +Pk2m78Z05IZHBkxcl20tZZR3G3hFTVqf7OemEucT5Kb/Vx6V++ZXY5eI54bh1oEJ +f8klr2DnscyTYdT33cmMuaxUm1sWjHeBBdQuQlMnW11XoCiGQMgMyf2fwbzd/og+ +vDPtORIMw+zedxaTsiOyNASLWB5ILDpUR9PTzz1tRIIOF5DnKttEe3SYPIqkLhxu +N7OtpVhor0QUulph8sS2uiilTVyaYVciOJK7Cqq2K015l9nlqGg/KI0GRIC9ty+k +wd+/Hdazp+YcLn3tL8jhekST/DAxK9VIb0DBvaboKr8UJw35nLOwA7smsij50l2S +kfpu9z/80gFbnSSQo7L8zD/kBzaPlup0H+h96rF3IjdCrnbveHlvJDo3GcPNxauw +rGUQXnnMzDVSTY179HfcgLZdsm3uOIBicEFaxXu2/L4Eof9yp8D/b6SeZeS41O7a +ICvLXxkiIfQHukHvqMLJ2SqKQ0J8zEXN0OBSLjaHUkBIOs3L4IFe2v8DMN798GrG +QzcxC0bmr9s6TvihlYFBbYkMZ3IYPT4SFZg92/pKTPlyD/Blc9oZm8QpQMGIMDKc +nWDLeqeCTXV6TL1mymqzwyzs9+4cXvEiM167FsLqk5tGRIyl4AR/dItELEtCWl3I +koIOUEk5rbJekOhTc85SFSQmCV0IebsUv0CpdWlmNeexNryLZu0r6kTUFWzpHcv/ +0yEaBQFLVx9QAfRSIiNt+yAgGnpMxxMxeHs6shVmuscZ0fV50GpOpA== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp index 642b115bee..1c16f47ad6 100644 --- a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp +++ b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp @@ -47,6 +47,11 @@ #include #include +#if !defined(QT_NO_SSL) && defined(QT_NO_OPENSSL) && defined(QT_BUILD_INTERNAL) +#include "private/qsslkey_p.h" +#define TEST_CRYPTO +#endif + class tst_QSslKey : public QObject { Q_OBJECT @@ -90,6 +95,11 @@ private slots: void passphraseChecks_data(); void passphraseChecks(); void noPassphraseChecks(); +#ifdef TEST_CRYPTO + void encrypt_data(); + void encrypt(); +#endif + #endif private: QString testDataDir; @@ -306,9 +316,6 @@ void tst_QSslKey::toEncryptedPemOrDer() QByteArray pwBytes(password.toLatin1()); if (type == QSsl::PrivateKey) { -#ifdef QT_NO_OPENSSL - QSKIP("Encrypted keys require support from the SSL backend"); -#endif QByteArray encryptedPem = key.toPem(pwBytes); QVERIFY(!encryptedPem.isEmpty()); QSslKey keyPem(encryptedPem, algorithm, QSsl::Pem, type, pwBytes); @@ -347,6 +354,7 @@ void tst_QSslKey::passphraseChecks_data() QTest::newRow("DES") << QString(testDataDir + "/rsa-with-passphrase-des.pem"); QTest::newRow("3DES") << QString(testDataDir + "/rsa-with-passphrase-3des.pem"); + QTest::newRow("RC2") << QString(testDataDir + "/rsa-with-passphrase-rc2.pem"); } void tst_QSslKey::passphraseChecks() @@ -379,9 +387,6 @@ void tst_QSslKey::passphraseChecks() QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key } -#ifdef QT_NO_OPENSSL - QEXPECT_FAIL("", "Encrypted keys require support from the SSL backend", Abort); -#endif { if (!keyFile.isOpen()) keyFile.open(QIODevice::ReadOnly); @@ -413,9 +418,6 @@ void tst_QSslKey::noPassphraseChecks() QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); QVERIFY(!key.isNull()); // empty passphrase => should be able to decode key } -#ifdef QT_NO_OPENSSL - QEXPECT_FAIL("", "Encrypted keys require support from the SSL backend", Abort); -#endif { if (!keyFile.isOpen()) keyFile.open(QIODevice::ReadOnly); @@ -426,6 +428,112 @@ void tst_QSslKey::noPassphraseChecks() } } +#ifdef TEST_CRYPTO +Q_DECLARE_METATYPE(QSslKeyPrivate::Cipher) + +void tst_QSslKey::encrypt_data() +{ + QTest::addColumn("cipher"); + QTest::addColumn("key"); + QTest::addColumn("plainText"); + QTest::addColumn("cipherText"); + + QTest::newRow("DES-CBC, length 0") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray() + << QByteArray::fromHex("956585228BAF9B1F"); + QTest::newRow("DES-CBC, length 1") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(1, 'a') + << QByteArray::fromHex("E6880AF202BA3C12"); + QTest::newRow("DES-CBC, length 2") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(2, 'a') + << QByteArray::fromHex("A82492386EED6026"); + QTest::newRow("DES-CBC, length 3") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(3, 'a') + << QByteArray::fromHex("90B76D5B79519CBA"); + QTest::newRow("DES-CBC, length 4") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(4, 'a') + << QByteArray::fromHex("63E3DD6FED87052A"); + QTest::newRow("DES-CBC, length 5") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(5, 'a') + << QByteArray::fromHex("03ACDB0EACBDFA94"); + QTest::newRow("DES-CBC, length 6") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(6, 'a') + << QByteArray::fromHex("7D95024E42A3A88A"); + QTest::newRow("DES-CBC, length 7") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(7, 'a') + << QByteArray::fromHex("5003436B8A8E42E9"); + QTest::newRow("DES-CBC, length 8") + << QSslKeyPrivate::DesCbc << QByteArray("01234567") + << QByteArray(8, 'a') + << QByteArray::fromHex("E4C1F054BF5521C0A4A0FD4A2BC6C1B1"); + + QTest::newRow("DES-EDE3-CBC, length 0") + << QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn") + << QByteArray() + << QByteArray::fromHex("3B2B4CD0B0FD495F"); + QTest::newRow("DES-EDE3-CBC, length 8") + << QSslKeyPrivate::DesEde3Cbc << QByteArray("0123456789abcdefghijklmn") + << QByteArray(8, 'a') + << QByteArray::fromHex("F2A5A87763C54A72A3224103D90CDB03"); + + QTest::newRow("RC2-40-CBC, length 0") + << QSslKeyPrivate::Rc2Cbc << QByteArray("01234") + << QByteArray() + << QByteArray::fromHex("6D05D52392FF6E7A"); + QTest::newRow("RC2-40-CBC, length 8") + << QSslKeyPrivate::Rc2Cbc << QByteArray("01234") + << QByteArray(8, 'a') + << QByteArray::fromHex("75768E64C5749072A5D168F3AFEB0005"); + + QTest::newRow("RC2-64-CBC, length 0") + << QSslKeyPrivate::Rc2Cbc << QByteArray("01234567") + << QByteArray() + << QByteArray::fromHex("ADAE6BF70F420130"); + QTest::newRow("RC2-64-CBC, length 8") + << QSslKeyPrivate::Rc2Cbc << QByteArray("01234567") + << QByteArray(8, 'a') + << QByteArray::fromHex("C7BF5C80AFBE9FBEFBBB9FD935F6D0DF"); + + QTest::newRow("RC2-128-CBC, length 0") + << QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg") + << QByteArray() + << QByteArray::fromHex("1E965D483A13C8FB"); + QTest::newRow("RC2-128-CBC, length 8") + << QSslKeyPrivate::Rc2Cbc << QByteArray("012345679abcdefg") + << QByteArray(8, 'a') + << QByteArray::fromHex("5AEC1A5B295660B02613454232F7DECE"); +} + +void tst_QSslKey::encrypt() +{ + QFETCH(QSslKeyPrivate::Cipher, cipher); + QFETCH(QByteArray, key); + QFETCH(QByteArray, plainText); + QFETCH(QByteArray, cipherText); + QByteArray iv("abcdefgh"); + +#ifdef Q_OS_WINRT + QEXPECT_FAIL("RC2-40-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-40-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-64-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-64-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort); +#endif + QByteArray encrypted = QSslKeyPrivate::encrypt(cipher, plainText, key, iv); + QCOMPARE(encrypted, cipherText); + + QByteArray decrypted = QSslKeyPrivate::decrypt(cipher, cipherText, key, iv); + QCOMPARE(decrypted, plainText); +} +#endif + #endif QTEST_MAIN(tst_QSslKey) -- cgit v1.2.3