summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2014-08-31 14:55:06 +0300
committerRichard J. Moore <rich@kde.org>2014-09-02 23:16:25 +0200
commitcd3dece750aa30b15091f211a72b6fcf67d49853 (patch)
tree36594c8282b07fde103d893da48a94201d67a8c5
parentb17365cda91a6ecc8d12e995d83f4ff479c59b5f (diff)
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 <andrew.knight@digia.com> Task-number: QTBUG-40688 Change-Id: I0d153425ce63601ff03b784a111e13962061025f Reviewed-by: Richard J. Moore <rich@kde.org>
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp3
-rw-r--r--src/network/ssl/qsslkey_p.cpp52
-rw-r--r--src/network/ssl/qsslkey_p.h13
-rw-r--r--src/network/ssl/qsslkey_qt.cpp78
-rw-r--r--src/network/ssl/qsslkey_winrt.cpp98
-rw-r--r--tests/auto/network/ssl/qsslkey/qsslkey.pro3
-rw-r--r--tests/auto/network/ssl/qsslkey/rsa-with-passphrase-rc2.pem18
-rw-r--r--tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp126
8 files changed, 367 insertions, 24 deletions
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<QByteArray, QByteArray> 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 <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qbytearraymatcher.h>
#include <QtCore/qiodevice.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
@@ -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<QByteArray, QByteArray> &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<QByteArray, QByteArray>::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<QByteArray, QByteArray> *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<QByteArray, QByteArray> 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<QByteArray, QByteArray> &headers) const;
+ QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *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 <QtCore/qcryptographichash.h>
+
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<QByteArray, QByteArray> headers;
+ QByteArray data = derFromPem(pem, &headers);
+ if (headers.value("Proc-Type") == "4,ENCRYPTED") {
+ QList<QByteArray> 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<QByteArray, QByteArray> 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<ISymmetricKeyAlgorithmProviderStatics> 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<ICryptographicEngineStatics> engine;
+ QHash<QSslKeyPrivate::Cipher, ComPtr<ISymmetricKeyAlgorithmProvider>> keyProviders;
+ ComPtr<ICryptographicBufferStatics> 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<IBuffer> keyBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(key.length(), (BYTE *)key.data(), &keyBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<ICryptographicKey> 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<IBuffer> dataBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(data.length(), (BYTE *)data.data(), &dataBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBuffer> ivBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(iv.length(), (BYTE *)iv.data(), &ivBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBuffer> 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<Windows::Storage::Streams::IBufferByteAccess> 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<const char *>(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 <QtNetwork/qhostaddress.h>
#include <QtNetwork/qnetworkproxy.h>
+#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<QSslKeyPrivate::Cipher>("cipher");
+ QTest::addColumn<QByteArray>("key");
+ QTest::addColumn<QByteArray>("plainText");
+ QTest::addColumn<QByteArray>("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)