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>bb10
parent
b17365cda9
commit
cd3dece750
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
decodeDer(derFromPem(pem), deepClear);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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-----
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue