1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44 #include <qsslkey.h>
45 #include <qsslsocket.h>
46
47 #include <QtNetwork/qhostaddress.h>
48 #include <QtNetwork/qnetworkproxy.h>
49
50 #ifdef Q_OS_SYMBIAN
51 // In Symbian OS test data is located in applications private dir
52 // Current path (C:\private\<UID>) contains only ascii chars
53 #define SRCDIR "."
54 #endif
55
56 class tst_QSslKey : public QObject
57 {
58 Q_OBJECT
59
60 struct KeyInfo {
61 QFileInfo fileInfo;
62 QSsl::KeyAlgorithm algorithm;
63 QSsl::KeyType type;
64 int length;
65 QSsl::EncodingFormat format;
KeyInfotst_QSslKey::KeyInfo66 KeyInfo(
67 const QFileInfo &fileInfo, QSsl::KeyAlgorithm algorithm, QSsl::KeyType type,
68 int length, QSsl::EncodingFormat format)
69 : fileInfo(fileInfo), algorithm(algorithm), type(type), length(length)
70 , format(format) {}
71 };
72
73 QList<KeyInfo> keyInfoList;
74
75 void createPlainTestRows();
76
77 public:
78 tst_QSslKey();
79 virtual ~tst_QSslKey();
80
81 public slots:
82 void initTestCase_data();
83 void init();
84 void cleanup();
85
86 #ifndef QT_NO_OPENSSL
87
88 private slots:
89 void emptyConstructor();
90 void constructor_data();
91 void constructor();
92 void copyAndAssign_data();
93 void copyAndAssign();
94 void equalsOperator();
95 void length_data();
96 void length();
97 void toPemOrDer_data();
98 void toPemOrDer();
99 void toEncryptedPemOrDer_data();
100 void toEncryptedPemOrDer();
101
102 void passphraseChecks();
103 #endif
104 };
105
tst_QSslKey()106 tst_QSslKey::tst_QSslKey()
107 {
108 #ifdef Q_WS_MAC
109 // applicationDirPath() points to a path inside the app bundle on Mac.
110 QDir dir(qApp->applicationDirPath() + QLatin1String("/../../../keys"));
111 #else
112 QDir dir(SRCDIR + QLatin1String("/keys")); // prefer this way to avoid ifdeffery and support shadow builds?
113 #endif
114 QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::Readable);
115 QRegExp rx(QLatin1String("^(rsa|dsa)-(pub|pri)-(\\d+)\\.(pem|der)$"));
116 foreach (QFileInfo fileInfo, fileInfoList) {
117 if (rx.indexIn(fileInfo.fileName()) >= 0)
118 keyInfoList << KeyInfo(
119 fileInfo,
120 rx.cap(1) == QLatin1String("rsa") ? QSsl::Rsa : QSsl::Dsa,
121 rx.cap(2) == QLatin1String("pub") ? QSsl::PublicKey : QSsl::PrivateKey,
122 rx.cap(3).toInt(),
123 rx.cap(4) == QLatin1String("pem") ? QSsl::Pem : QSsl::Der);
124 }
125 }
126
~tst_QSslKey()127 tst_QSslKey::~tst_QSslKey()
128 {
129 }
130
initTestCase_data()131 void tst_QSslKey::initTestCase_data()
132 {
133 }
134
init()135 void tst_QSslKey::init()
136 {
137 }
138
cleanup()139 void tst_QSslKey::cleanup()
140 {
141 }
142
readFile(const QString & absFilePath)143 static QByteArray readFile(const QString &absFilePath)
144 {
145 QFile file(absFilePath);
146 if (!file.open(QIODevice::ReadOnly)) {
147 QWARN("failed to open file");
148 return QByteArray();
149 }
150 return file.readAll();
151 }
152
153 #ifndef QT_NO_OPENSSL
154
emptyConstructor()155 void tst_QSslKey::emptyConstructor()
156 {
157 if (!QSslSocket::supportsSsl())
158 return;
159
160 QSslKey key;
161 QVERIFY(key.isNull());
162 QVERIFY(key.length() < 0);
163
164 QSslKey key2;
165 QCOMPARE(key, key2);
166 }
167
168 Q_DECLARE_METATYPE(QSsl::KeyAlgorithm)
Q_DECLARE_METATYPE(QSsl::KeyType)169 Q_DECLARE_METATYPE(QSsl::KeyType)
170 Q_DECLARE_METATYPE(QSsl::EncodingFormat)
171
172 void tst_QSslKey::createPlainTestRows()
173 {
174 QTest::addColumn<QString>("absFilePath");
175 QTest::addColumn<QSsl::KeyAlgorithm>("algorithm");
176 QTest::addColumn<QSsl::KeyType>("type");
177 QTest::addColumn<int>("length");
178 QTest::addColumn<QSsl::EncodingFormat>("format");
179 foreach (KeyInfo keyInfo, keyInfoList) {
180 QTest::newRow(keyInfo.fileInfo.fileName().toLatin1())
181 << keyInfo.fileInfo.absoluteFilePath() << keyInfo.algorithm << keyInfo.type
182 << keyInfo.length << keyInfo.format;
183 }
184 }
185
constructor_data()186 void tst_QSslKey::constructor_data()
187 {
188 createPlainTestRows();
189 }
190
constructor()191 void tst_QSslKey::constructor()
192 {
193 if (!QSslSocket::supportsSsl())
194 return;
195
196 QFETCH(QString, absFilePath);
197 QFETCH(QSsl::KeyAlgorithm, algorithm);
198 QFETCH(QSsl::KeyType, type);
199 QFETCH(QSsl::EncodingFormat, format);
200
201 QByteArray encoded = readFile(absFilePath);
202 QSslKey key(encoded, algorithm, format, type);
203 QVERIFY(!key.isNull());
204 }
205
copyAndAssign_data()206 void tst_QSslKey::copyAndAssign_data()
207 {
208 createPlainTestRows();
209 }
210
copyAndAssign()211 void tst_QSslKey::copyAndAssign()
212 {
213 if (!QSslSocket::supportsSsl())
214 return;
215
216 QFETCH(QString, absFilePath);
217 QFETCH(QSsl::KeyAlgorithm, algorithm);
218 QFETCH(QSsl::KeyType, type);
219 QFETCH(QSsl::EncodingFormat, format);
220
221 QByteArray encoded = readFile(absFilePath);
222 QSslKey key(encoded, algorithm, format, type);
223
224 QSslKey copied(key);
225 QCOMPARE(key, copied);
226 QCOMPARE(key.algorithm(), copied.algorithm());
227 QCOMPARE(key.type(), copied.type());
228 QCOMPARE(key.length(), copied.length());
229 QCOMPARE(key.toPem(), copied.toPem());
230 QCOMPARE(key.toDer(), copied.toDer());
231
232 QSslKey assigned = key;
233 QCOMPARE(key, assigned);
234 QCOMPARE(key.algorithm(), assigned.algorithm());
235 QCOMPARE(key.type(), assigned.type());
236 QCOMPARE(key.length(), assigned.length());
237 QCOMPARE(key.toPem(), assigned.toPem());
238 QCOMPARE(key.toDer(), assigned.toDer());
239 }
240
equalsOperator()241 void tst_QSslKey::equalsOperator()
242 {
243 // ### unimplemented
244 }
245
length_data()246 void tst_QSslKey::length_data()
247 {
248 createPlainTestRows();
249 }
250
length()251 void tst_QSslKey::length()
252 {
253 if (!QSslSocket::supportsSsl())
254 return;
255
256 QFETCH(QString, absFilePath);
257 QFETCH(QSsl::KeyAlgorithm, algorithm);
258 QFETCH(QSsl::KeyType, type);
259 QFETCH(int, length);
260 QFETCH(QSsl::EncodingFormat, format);
261
262 QByteArray encoded = readFile(absFilePath);
263 QSslKey key(encoded, algorithm, format, type);
264 QVERIFY(!key.isNull());
265 QCOMPARE(key.length(), length);
266 }
267
toPemOrDer_data()268 void tst_QSslKey::toPemOrDer_data()
269 {
270 createPlainTestRows();
271 }
272
toPemOrDer()273 void tst_QSslKey::toPemOrDer()
274 {
275 if (!QSslSocket::supportsSsl())
276 return;
277
278 QFETCH(QString, absFilePath);
279 QFETCH(QSsl::KeyAlgorithm, algorithm);
280 QFETCH(QSsl::KeyType, type);
281 QFETCH(QSsl::EncodingFormat, format);
282
283 QByteArray encoded = readFile(absFilePath);
284 QSslKey key(encoded, algorithm, format, type);
285 QVERIFY(!key.isNull());
286 if (format == QSsl::Pem)
287 encoded.replace('\r', "");
288 QCOMPARE(format == QSsl::Pem ? key.toPem() : key.toDer(), encoded);
289 }
290
toEncryptedPemOrDer_data()291 void tst_QSslKey::toEncryptedPemOrDer_data()
292 {
293 QTest::addColumn<QString>("absFilePath");
294 QTest::addColumn<QSsl::KeyAlgorithm>("algorithm");
295 QTest::addColumn<QSsl::KeyType>("type");
296 QTest::addColumn<QSsl::EncodingFormat>("format");
297 QTest::addColumn<QString>("password");
298
299 QStringList passwords;
300 passwords << " " << "foobar" << "foo bar"
301 << "aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?"; // ### add more (?)
302 foreach (KeyInfo keyInfo, keyInfoList) {
303 foreach (QString password, passwords) {
304 QString testName = QString("%1-%2-%3-%4").arg(keyInfo.fileInfo.fileName())
305 .arg(keyInfo.algorithm == QSsl::Rsa ? "RSA" : "DSA")
306 .arg(keyInfo.type == QSsl::PrivateKey ? "PrivateKey" : "PublicKey")
307 .arg(keyInfo.format == QSsl::Pem ? "PEM" : "DER");
308 QTest::newRow(testName.toLatin1())
309 << keyInfo.fileInfo.absoluteFilePath() << keyInfo.algorithm << keyInfo.type
310 << keyInfo.format << password;
311 }
312 }
313 }
314
toEncryptedPemOrDer()315 void tst_QSslKey::toEncryptedPemOrDer()
316 {
317 if (!QSslSocket::supportsSsl())
318 return;
319
320 QFETCH(QString, absFilePath);
321 QFETCH(QSsl::KeyAlgorithm, algorithm);
322 QFETCH(QSsl::KeyType, type);
323 QFETCH(QSsl::EncodingFormat, format);
324 QFETCH(QString, password);
325
326 QByteArray plain = readFile(absFilePath);
327 QSslKey key(plain, algorithm, format, type);
328 QVERIFY(!key.isNull());
329
330 QByteArray pwBytes(password.toLatin1());
331
332 if (type == QSsl::PrivateKey) {
333 QByteArray encryptedPem = key.toPem(pwBytes);
334 QVERIFY(!encryptedPem.isEmpty());
335 QSslKey keyPem(encryptedPem, algorithm, QSsl::Pem, type, pwBytes);
336 QVERIFY(!keyPem.isNull());
337 QCOMPARE(keyPem, key);
338 QCOMPARE(keyPem.toPem(), key.toPem());
339 } else {
340 // verify that public keys are never encrypted by toPem()
341 QByteArray encryptedPem = key.toPem(pwBytes);
342 QVERIFY(!encryptedPem.isEmpty());
343 QByteArray plainPem = key.toPem();
344 QVERIFY(!plainPem.isEmpty());
345 QCOMPARE(encryptedPem, plainPem);
346 }
347
348 if (type == QSsl::PrivateKey) {
349 QByteArray encryptedDer = key.toDer(pwBytes);
350 // ### at this point, encryptedDer is invalid, hence the below QEXPECT_FAILs
351 QVERIFY(!encryptedDer.isEmpty());
352 QSslKey keyDer(encryptedDer, algorithm, QSsl::Der, type, pwBytes);
353 if (type == QSsl::PrivateKey)
354 QEXPECT_FAIL(
355 QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue);
356 QVERIFY(!keyDer.isNull());
357 if (type == QSsl::PrivateKey)
358 QEXPECT_FAIL(
359 QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue);
360 QCOMPARE(keyDer.toPem(), key.toPem());
361 } else {
362 // verify that public keys are never encrypted by toDer()
363 QByteArray encryptedDer = key.toDer(pwBytes);
364 QVERIFY(!encryptedDer.isEmpty());
365 QByteArray plainDer = key.toDer();
366 QVERIFY(!plainDer.isEmpty());
367 QCOMPARE(encryptedDer, plainDer);
368 }
369
370 // ### add a test to verify that public keys are _decrypted_ correctly (by the ctor)
371 }
372
passphraseChecks()373 void tst_QSslKey::passphraseChecks()
374 {
375 {
376 QString fileName(SRCDIR "/rsa-with-passphrase.pem");
377 QFile keyFile(fileName);
378 QVERIFY(keyFile.exists());
379 {
380 if (!keyFile.isOpen())
381 keyFile.open(QIODevice::ReadOnly);
382 else
383 keyFile.reset();
384 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey);
385 QVERIFY(key.isNull()); // null passphrase => should not be able to decode key
386 }
387 {
388 if (!keyFile.isOpen())
389 keyFile.open(QIODevice::ReadOnly);
390 else
391 keyFile.reset();
392 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "");
393 QVERIFY(key.isNull()); // empty passphrase => should not be able to decode key
394 }
395 {
396 if (!keyFile.isOpen())
397 keyFile.open(QIODevice::ReadOnly);
398 else
399 keyFile.reset();
400 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!");
401 QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key
402 }
403 {
404 if (!keyFile.isOpen())
405 keyFile.open(QIODevice::ReadOnly);
406 else
407 keyFile.reset();
408 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "123");
409 QVERIFY(!key.isNull()); // correct passphrase
410 }
411 }
412
413 {
414 // be sure and check a key without passphrase too
415 QString fileName(SRCDIR "/rsa-without-passphrase.pem");
416 QFile keyFile(fileName);
417 {
418 if (!keyFile.isOpen())
419 keyFile.open(QIODevice::ReadOnly);
420 else
421 keyFile.reset();
422 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey);
423 QVERIFY(!key.isNull()); // null passphrase => should be able to decode key
424 }
425 {
426 if (!keyFile.isOpen())
427 keyFile.open(QIODevice::ReadOnly);
428 else
429 keyFile.reset();
430 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "");
431 QVERIFY(!key.isNull()); // empty passphrase => should be able to decode key
432 }
433 {
434 if (!keyFile.isOpen())
435 keyFile.open(QIODevice::ReadOnly);
436 else
437 keyFile.reset();
438 QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "xxx");
439 QVERIFY(!key.isNull()); // passphrase given but key is not encrypted anyway => should work
440 }
441 }
442 }
443
444 #endif
445
446 QTEST_MAIN(tst_QSslKey)
447 #include "tst_qsslkey.moc"
448