1/****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include <QtNetwork/private/qtnetworkglobal_p.h>
32
33#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
34#include <QtNetwork/private/qsslsocket_openssl_p.h>
35
36#include <QtNetwork/qsslcertificate.h>
37#include <QtNetwork/qtcpserver.h>
38#include <QtNetwork/qsslerror.h>
39#include <QtNetwork/qsslkey.h>
40#include <QtNetwork/qssl.h>
41
42#include <QtCore/qsharedpointer.h>
43#include <QtCore/qbytearray.h>
44#include <QtCore/qfileinfo.h>
45#include <QtCore/qstring.h>
46#include <QtCore/qfile.h>
47#include <QtCore/qlist.h>
48#include <QtCore/qdir.h>
49
50#include <openssl/ocsp.h>
51
52#include <algorithm>
53#include <utility>
54
55// NOTE: the word 'subject' in the code below means the subject of a status request,
56// so in general it's our peer's certificate we are asking about.
57
58using SslError = QT_PREPEND_NAMESPACE(QSslError);
59using VectorOfErrors = QT_PREPEND_NAMESPACE(QVector<SslError>);
60using Latin1String = QT_PREPEND_NAMESPACE(QLatin1String);
61
62Q_DECLARE_METATYPE(SslError)
63Q_DECLARE_METATYPE(VectorOfErrors)
64Q_DECLARE_METATYPE(Latin1String)
65
66QT_BEGIN_NAMESPACE
67
68namespace {
69
70using OcspResponse = QSharedPointer<OCSP_RESPONSE>;
71using BasicResponse = QSharedPointer<OCSP_BASICRESP>;
72using SingleResponse = QSharedPointer<OCSP_SINGLERESP>;
73using CertId = QSharedPointer<OCSP_CERTID>;
74using EvpKey = QSharedPointer<EVP_PKEY>;
75using Asn1Time = QSharedPointer<ASN1_TIME>;
76using CertificateChain = QList<QSslCertificate>;
77
78using NativeX509Ptr = X509 *;
79
80class X509Stack {
81public:
82 explicit X509Stack(const QList<QSslCertificate> &chain);
83
84 ~X509Stack();
85
86 int size() const;
87 X509 *operator[](int index) const;
88 operator STACK_OF(X509) *() const;
89
90private:
91 OPENSSL_STACK *stack = nullptr;
92
93 Q_DISABLE_COPY(X509Stack)
94};
95
96X509Stack::X509Stack(const QList<QSslCertificate> &chain)
97{
98 if (!chain.size())
99 return;
100
101 stack = q_OPENSSL_sk_new_null();
102 if (!stack)
103 return;
104
105 for (const QSslCertificate &cert : chain) {
106 X509 *nativeCert = NativeX509Ptr(cert.handle());
107 if (!nativeCert)
108 continue;
109 q_OPENSSL_sk_push(st: stack, data: nativeCert);
110 q_X509_up_ref(a: nativeCert);
111 }
112}
113
114X509Stack::~X509Stack()
115{
116 if (stack)
117 q_OPENSSL_sk_pop_free(a: stack, b: reinterpret_cast<void(*)(void*)>(q_X509_free));
118}
119
120int X509Stack::size() const
121{
122 if (stack)
123 return q_OPENSSL_sk_num(a: stack);
124 return 0;
125}
126
127X509 *X509Stack::operator[](int index) const
128{
129 return NativeX509Ptr(q_OPENSSL_sk_value(a: stack, b: index));
130}
131
132X509Stack::operator STACK_OF(X509) *() const
133{
134 return reinterpret_cast<STACK_OF(X509)*>(stack);
135}
136
137struct OcspTimeStamp
138{
139 OcspTimeStamp() = default;
140 OcspTimeStamp(long secondsBeforeNow, long secondsAfterNow);
141
142 static Asn1Time timeToAsn1Time(long adjustment);
143
144 Asn1Time thisUpdate;
145 Asn1Time nextUpdate;
146};
147
148OcspTimeStamp::OcspTimeStamp(long secondsBeforeNow, long secondsAfterNow)
149{
150 Asn1Time start = timeToAsn1Time(adjustment: secondsBeforeNow);
151 Asn1Time end = timeToAsn1Time(adjustment: secondsAfterNow);
152 if (start.data() && end.data()) {
153 thisUpdate.swap(other&: start);
154 nextUpdate.swap(other&: end);
155 }
156}
157
158Asn1Time OcspTimeStamp::timeToAsn1Time(long adjustment)
159{
160 if (ASN1_TIME *adjusted = q_X509_gmtime_adj(s: nullptr, adj: adjustment))
161 return Asn1Time(adjusted, q_ASN1_TIME_free);
162 return Asn1Time{};
163}
164
165struct OcspResponder
166{
167 OcspResponder(const OcspTimeStamp &stamp, const CertificateChain &subjs,
168 const CertificateChain &respChain, const QSslKey &respPKey);
169
170 QByteArray buildResponse(int responseStatus, int certificateStatus) const;
171 static EvpKey privateKeyToEVP_PKEY(const QSslKey &privateKey);
172 static CertId certificateToCertId(X509 *subject, X509 *issuer);
173 static QByteArray responseToDer(OCSP_RESPONSE *response);
174
175 OcspTimeStamp timeStamp;
176 // Plural, we can send a 'wrong' BasicResponse containing more than
177 // 1 SingleResponse.
178 X509Stack subjects;
179 X509Stack responderChain;
180 QSslKey responderKey;
181};
182
183OcspResponder::OcspResponder(const OcspTimeStamp &stamp, const CertificateChain &subjs,
184 const CertificateChain &respChain, const QSslKey &respPKey)
185 : timeStamp(stamp),
186 subjects(subjs),
187 responderChain(respChain),
188 responderKey(respPKey)
189{
190}
191
192QByteArray OcspResponder::buildResponse(int responseStatus, int certificateStatus) const
193{
194 if (responseStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
195 OCSP_RESPONSE *response = q_OCSP_response_create(status: responseStatus, bs: nullptr);
196 if (!response)
197 return {};
198 const OcspResponse rGuard(response, q_OCSP_RESPONSE_free);
199 return responseToDer(response);
200 }
201
202 Q_ASSERT(subjects.size() && responderChain.size() && responderKey.handle());
203
204 const EvpKey nativeKey = privateKeyToEVP_PKEY(privateKey: responderKey);
205 if (!nativeKey.data())
206 return {};
207
208 OCSP_BASICRESP *basicResponse = q_OCSP_BASICRESP_new();
209 if (!basicResponse)
210 return {};
211 const BasicResponse brGuard(basicResponse, q_OCSP_BASICRESP_free);
212
213 for (int i = 0, e = subjects.size(); i < e; ++i) {
214 X509 *subject = subjects[i];
215 Q_ASSERT(subject);
216 CertId certId = certificateToCertId(subject, issuer: responderChain[0]);
217 if (!certId.data())
218 return {};
219
220 // NOTE: we do not own this 'singleResponse':
221 ASN1_TIME *revisionTime = certificateStatus == V_OCSP_CERTSTATUS_REVOKED ?
222 timeStamp.thisUpdate.data() : nullptr;
223
224 if (!q_OCSP_basic_add1_status(rsp: basicResponse, cid: certId.data(), status: certificateStatus, reason: 0, revtime: revisionTime,
225 thisupd: timeStamp.thisUpdate.data(), nextupd: timeStamp.nextUpdate.data())) {
226 return {};
227 }
228 }
229
230 if (q_OCSP_basic_sign(brsp: basicResponse, signer: responderChain[0], key: nativeKey.data(), dgst: q_EVP_sha1(),
231 certs: responderChain, flags: 0) != 1) {
232 return {};
233 }
234
235 OCSP_RESPONSE *ocspResponse = q_OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs: basicResponse);
236 if (!ocspResponse)
237 return {};
238 const OcspResponse rGuard(ocspResponse, q_OCSP_RESPONSE_free);
239 return responseToDer(response: ocspResponse);
240}
241
242EvpKey OcspResponder::privateKeyToEVP_PKEY(const QSslKey &privateKey)
243{
244 const EvpKey nullKey;
245 if (privateKey.isNull() || privateKey.algorithm() != QSsl::Rsa) {
246 // We use only RSA keys in this auto-test, since we test OCSP only,
247 // not handshake/TLS in general.
248 return nullKey;
249 }
250
251 EVP_PKEY *nativeKey = q_EVP_PKEY_new();
252 if (!nativeKey)
253 return nullKey;
254
255 const EvpKey keyGuard(nativeKey, q_EVP_PKEY_free);
256 if (!q_EVP_PKEY_set1_RSA(a: nativeKey, b: reinterpret_cast<RSA *>(privateKey.handle())))
257 return nullKey;
258
259 return keyGuard;
260}
261
262CertId OcspResponder::certificateToCertId(X509 *subject, X509 *issuer)
263{
264 const CertId nullId;
265 if (!subject || !issuer)
266 return nullId;
267
268 const EVP_MD *digest = q_EVP_sha1();
269 if (!digest)
270 return nullId;
271
272 OCSP_CERTID *certId = q_OCSP_cert_to_id(dgst: digest, subject, issuer);
273 if (!certId)
274 return nullId;
275
276 return CertId(certId, q_OCSP_CERTID_free);
277}
278
279QByteArray OcspResponder::responseToDer(OCSP_RESPONSE *response)
280{
281 if (!response)
282 return {};
283
284 const int derSize = q_i2d_OCSP_RESPONSE(r: response, ppout: nullptr);
285 if (derSize <= 0)
286 return {};
287
288 QByteArray derData(derSize, Qt::Uninitialized);
289 unsigned char *pData = reinterpret_cast<unsigned char *>(derData.data());
290 const int serializedSize = q_i2d_OCSP_RESPONSE(r: response, ppout: &pData);
291 if (serializedSize != derSize)
292 return {};
293
294 return derData;
295}
296
297// The QTcpServer capable of sending OCSP status responses.
298class OcspServer : public QTcpServer
299{
300 Q_OBJECT
301
302public:
303 OcspServer(const CertificateChain &serverChain, const QSslKey &privateKey);
304
305 void configureResponse(const QByteArray &responseDer);
306 QString hostName() const;
307 QString peerVerifyName() const;
308
309Q_SIGNALS:
310 void internalServerError();
311
312private:
313 void incomingConnection(qintptr descriptor) override;
314
315public:
316 QSslConfiguration serverConfig;
317 QSslSocket serverSocket;
318};
319
320OcspServer::OcspServer(const CertificateChain &serverChain, const QSslKey &privateKey)
321{
322 Q_ASSERT(serverChain.size());
323 Q_ASSERT(!privateKey.isNull());
324
325 serverConfig = QSslConfiguration::defaultConfiguration();
326 serverConfig.setLocalCertificateChain(serverChain);
327 serverConfig.setPrivateKey(privateKey);
328}
329
330void OcspServer::configureResponse(const QByteArray &responseDer)
331{
332 serverConfig.setBackendConfigurationOption(name: "Qt-OCSP-response", value: responseDer);
333}
334
335QString OcspServer::hostName() const
336{
337 // It's 'name' and not 'address' to be consistent with QSslSocket's naming style,
338 // where it's connectToHostEncrypted(hostName, ...)
339 const QHostAddress &addr = serverAddress();
340 if (addr == QHostAddress::Any || addr == QHostAddress::AnyIPv4)
341 return QStringLiteral("127.0.0.1");
342 if (addr == QHostAddress::AnyIPv6)
343 return QStringLiteral("::1");
344 return addr.toString();
345}
346
347QString OcspServer::peerVerifyName() const
348{
349 const CertificateChain &localChain = serverConfig.localCertificateChain();
350 if (localChain.isEmpty())
351 return {};
352 const auto cert = localChain.first();
353 if (cert.isNull())
354 return {};
355
356 const QStringList &names = cert.subjectInfo(info: QSslCertificate::CommonName);
357 return names.isEmpty() ? QString{} : names.first();
358}
359
360void OcspServer::incomingConnection(qintptr socketDescriptor)
361{
362 close();
363
364 if (!serverSocket.setSocketDescriptor(socketDescriptor)) {
365 emit internalServerError();
366 return;
367 }
368
369 serverSocket.setSslConfiguration(serverConfig);
370 // Since we test a client, not a server, we don't care about any
371 // possible errors on the server (QAbstractSocket or QSslSocket-related).
372 // Thus, we don't connect to any error signal.
373 serverSocket.startServerEncryption();
374}
375
376} // unnamed namespace
377
378class tst_QOcsp : public QObject
379{
380 Q_OBJECT
381
382public slots:
383 void initTestCase();
384
385private slots:
386 void connectSelfSigned();
387 void badStatus_data();
388 void badStatus();
389 void multipleSingleResponses();
390 void malformedResponse();
391 void expiredResponse_data();
392 void expiredResponse();
393 void noNextUpdate();
394 void wrongCertificateInResponse_data();
395 void wrongCertificateInResponse();
396 void untrustedResponder();
397
398 // OCSPTODO: more tests in future ...
399
400private:
401 void setupOcspClient(QSslSocket &clientSocket, const CertificateChain &trustedCAs,
402 const QString &peerName);
403 bool containsOcspErrors(const QList<QSslError> &errorsFound) const;
404 static bool containsError(const QList<QSslError> &errors, QSslError::SslError code);
405 static QByteArray goodResponse(const CertificateChain &subject, const CertificateChain &responder,
406 const QSslKey &privateKey, long beforeNow = -1000, long afterNow = 1000);
407 static bool loadPrivateKey(const QString &keyName, QSslKey &key);
408 static CertificateChain issuerToChain(const CertificateChain &chain);
409 static CertificateChain subjectToChain(const CertificateChain &chain);
410
411 static QString certDirPath;
412
413 void (QSslSocket::*tlsErrorsSignal)(const QList<QSslError> &) = &QSslSocket::sslErrors;
414 void (QTestEventLoop::*exitLoopSlot)() = &QTestEventLoop::exitLoop;
415
416 const int handshakeTimeoutMS = 500;
417 QTestEventLoop loop;
418
419 std::vector<QSslError::SslError> ocspErrorCodes = {QSslError::OcspNoResponseFound,
420 QSslError::OcspMalformedRequest,
421 QSslError::OcspMalformedResponse,
422 QSslError::OcspInternalError,
423 QSslError::OcspTryLater,
424 QSslError::OcspSigRequred,
425 QSslError::OcspUnauthorized,
426 QSslError::OcspResponseCannotBeTrusted,
427 QSslError::OcspResponseCertIdUnknown,
428 QSslError::OcspResponseExpired,
429 QSslError::OcspStatusUnknown};
430};
431
432#define QCOMPARE_SINGLE_ERROR(sslSocket, expectedError) \
433 const auto &tlsErrors = sslSocket.sslHandshakeErrors(); \
434 QCOMPARE(tlsErrors.size(), 1); \
435 QCOMPARE(tlsErrors[0].error(), expectedError)
436
437#define QVERIFY_HANDSHAKE_WITHOUT_ERRORS(sslSocket) \
438 QVERIFY(sslSocket.isEncrypted()); \
439 QCOMPARE(sslSocket.state(), QAbstractSocket::ConnectedState); \
440 QVERIFY(sslSocket.sslHandshakeErrors().isEmpty())
441
442#define QDECLARE_CHAIN(object, chainFileName) \
443 CertificateChain object = QSslCertificate::fromPath(certDirPath + QLatin1String(chainFileName)); \
444 QVERIFY(object.size())
445
446#define QDECLARE_PRIVATE_KEY(key, keyFileName) \
447 QSslKey key; \
448 QVERIFY(loadPrivateKey(QLatin1String(keyFileName), key))
449
450QString tst_QOcsp::certDirPath;
451
452void tst_QOcsp::initTestCase()
453{
454 QVERIFY(QSslSocket::supportsSsl());
455
456 certDirPath = QFileInfo(QFINDTESTDATA("certs")).absolutePath();
457 QVERIFY(certDirPath.size() > 0);
458 certDirPath += QDir::separator() + QStringLiteral("certs") + QDir::separator();
459}
460
461void tst_QOcsp::connectSelfSigned()
462{
463 // This test may look a bit confusing, since we have essentially 1
464 // self-signed certificate, which we trust for the purpose of this test,
465 // but we also request its (the certificate's) status and then we sign
466 // the status response using the same certificate and the corresponding
467 // private key. Anyway, we test the very basic things here: we send
468 // an OCSP status request, we verify the response (if server has sent it),
469 // and detect errors (if any).
470 QDECLARE_CHAIN(subjectChain, "ss1.crt");
471 QDECLARE_CHAIN(responderChain, "ss1.crt");
472 QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
473 {
474 // This server ignores our status request:
475 const QSslError::SslError expectedError = QSslError::OcspNoResponseFound;
476
477 OcspServer server(subjectChain, privateKey);
478 QVERIFY(server.listen());
479 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
480
481 QSslSocket clientSocket;
482 QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
483 auto roots = clientConfig.caCertificates();
484 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
485 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
486 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
487
488 QVERIFY(!clientSocket.isEncrypted());
489 QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
490 }
491 {
492 // Now the server will send a valid 'status: good' response.
493 OcspServer server(subjectChain, privateKey);
494 const QByteArray responseData(goodResponse(subject: subjectChain, responder: responderChain, privateKey));
495 QVERIFY(responseData.size());
496 server.configureResponse(responseDer: responseData);
497 QVERIFY(server.listen());
498
499 QSslSocket clientSocket;
500 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
501 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
502 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
503
504 QVERIFY_HANDSHAKE_WITHOUT_ERRORS(clientSocket);
505
506 const auto responses = clientSocket.ocspResponses();
507 QCOMPARE(responses.size(), 1);
508 const auto &response = responses.at(i: 0);
509 QVERIFY(response != QOcspResponse());
510 const auto copy = response;
511 QCOMPARE(copy, response);
512 QVERIFY(qHash(response, 0) != 0);
513
514 QCOMPARE(response.revocationReason(), QOcspRevocationReason::None);
515 QCOMPARE(response.certificateStatus(), QOcspCertificateStatus::Good);
516 QCOMPARE(response.subject(), clientSocket.peerCertificate());
517 QCOMPARE(response.responder(), clientSocket.peerCertificate());
518 }
519}
520
521void tst_QOcsp::badStatus_data()
522{
523 QTest::addColumn<int>(name: "responseStatus");
524 QTest::addColumn<int>(name: "certificateStatus");
525 QTest::addColumn<QSslError>(name: "expectedError");
526
527 QTest::addRow(format: "malformed-request") << OCSP_RESPONSE_STATUS_MALFORMEDREQUEST << 1 << QSslError(QSslError::OcspMalformedRequest);
528 QTest::addRow(format: "internal-error") << OCSP_RESPONSE_STATUS_INTERNALERROR << 2 << QSslError(QSslError::OcspInternalError);
529 QTest::addRow(format: "try-later") << OCSP_RESPONSE_STATUS_TRYLATER << 3 << QSslError(QSslError::OcspTryLater);
530 QTest::addRow(format: "signed-request-require") << OCSP_RESPONSE_STATUS_SIGREQUIRED << 2 << QSslError(QSslError::OcspSigRequred);
531 QTest::addRow(format: "unauthorized-request") << OCSP_RESPONSE_STATUS_UNAUTHORIZED << 1 <<QSslError(QSslError::OcspUnauthorized);
532
533 QTest::addRow(format: "certificate-revoked") << OCSP_RESPONSE_STATUS_SUCCESSFUL << V_OCSP_CERTSTATUS_REVOKED
534 << QSslError(QSslError::CertificateRevoked);
535 QTest::addRow(format: "status-unknown") << OCSP_RESPONSE_STATUS_SUCCESSFUL << V_OCSP_CERTSTATUS_UNKNOWN
536 << QSslError(QSslError::OcspStatusUnknown);
537}
538
539void tst_QOcsp::badStatus()
540{
541 // This test works with two types of 'bad' responses:
542 // 1. 'Error messages' (the response's status is anything but SUCCESSFUL,
543 // no information about the certificate itself, no signature);
544 // 2. 'REVOKED' or 'UNKNOWN' status for a certificate in question.
545 QFETCH(const int, responseStatus);
546 QFETCH(const int, certificateStatus);
547 QFETCH(const QSslError, expectedError);
548
549 QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
550 QCOMPARE(subjectChain.size(), 2);
551 QDECLARE_CHAIN(responderChain, "ca1.crt");
552 QDECLARE_PRIVATE_KEY(subjPrivateKey, "infbob.key");
553 QDECLARE_PRIVATE_KEY(respPrivateKey, "ca1.key");
554
555 OcspServer server(subjectChain, subjPrivateKey);
556 const OcspTimeStamp stamp(-1000, 1000);
557 OcspResponder builder(stamp, subjectToChain(chain: subjectChain), responderChain, respPrivateKey);
558 const QByteArray response(builder.buildResponse(responseStatus, certificateStatus));
559 QVERIFY(response.size());
560 server.configureResponse(responseDer: response);
561 QVERIFY(server.listen());
562 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
563
564 QSslSocket clientSocket;
565 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
566 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
567 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
568
569 QVERIFY(!clientSocket.isEncrypted());
570 QCOMPARE_SINGLE_ERROR(clientSocket, expectedError.error());
571}
572
573void tst_QOcsp::multipleSingleResponses()
574{
575 // We handle a response with more than one SingleResponse as malformed:
576 const QSslError::SslError expectedError = QSslError::OcspMalformedResponse;
577
578 // Here we use subjectChain only to generate a response, the server
579 // is configured with the responder chain (it's the same cert after all).
580 QDECLARE_CHAIN(subjectChain, "ss1.crt");
581 QDECLARE_CHAIN(responderChain, "ss1.crt");
582 QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
583
584 // Let's have more than 1 certificate in a chain:
585 subjectChain.append(t: subjectChain[0]);
586
587 OcspServer server(responderChain, privateKey);
588 // Generate a BasicOCSPResponse containing 2 SingleResponses:
589 const QByteArray response(goodResponse(subject: subjectChain, responder: responderChain, privateKey));
590 QVERIFY(response.size());
591 server.configureResponse(responseDer: response);
592 QVERIFY(server.listen());
593 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
594
595 QSslSocket clientSocket;
596 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: responderChain), peerName: server.peerVerifyName());
597 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
598 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
599
600 QVERIFY(!clientSocket.isEncrypted());
601 QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
602}
603
604void tst_QOcsp::malformedResponse()
605{
606 QDECLARE_CHAIN(serverChain, "ss1.crt");
607 QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
608
609 OcspServer server(serverChain, privateKey);
610 // Let's send some arbitrary bytes instead of DER and see what happens next:
611 server.configureResponse(responseDer: "Sure, you can trust me, this cert was not revoked (I don't say it was issued at all)!");
612 QVERIFY(server.listen());
613 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
614
615 QSslSocket clientSocket;
616 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: serverChain), peerName: server.peerVerifyName());
617 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
618 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
619
620 QVERIFY(!clientSocket.isEncrypted());
621 QCOMPARE(clientSocket.error(), QAbstractSocket::SslHandshakeFailedError);
622}
623
624void tst_QOcsp::expiredResponse_data()
625{
626 QTest::addColumn<long>(name: "beforeNow");
627 QTest::addColumn<long>(name: "afterNow");
628
629 QTest::addRow(format: "expired") << -2000L << -1000L;
630 QTest::addRow(format: "not-valid-yet") << 5000L << 10000L;
631 QTest::addRow(format: "next-before-this") << -1000L << -2000L;
632}
633
634void tst_QOcsp::expiredResponse()
635{
636 // We report different kinds of problems with [thisUpdate, nextUpdate]
637 // as 'expired' (to keep it simple):
638 const QSslError::SslError expectedError = QSslError::OcspResponseExpired;
639
640 QFETCH(const long, beforeNow);
641 QFETCH(const long, afterNow);
642
643 QDECLARE_CHAIN(subjectChain, "ss1.crt");
644 QDECLARE_CHAIN(responderChain, "ss1.crt");
645 QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
646
647 OcspServer server(subjectChain, privateKey);
648 const QByteArray response(goodResponse(subject: subjectChain, responder: responderChain, privateKey, beforeNow, afterNow));
649 QVERIFY(response.size());
650 server.configureResponse(responseDer: response);
651 QVERIFY(server.listen());
652 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
653
654 QSslSocket clientSocket;
655 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
656 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
657 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
658
659 QVERIFY(!clientSocket.isEncrypted());
660 QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
661}
662
663void tst_QOcsp::noNextUpdate()
664{
665 // RFC2560, 2.4:
666 // "If nextUpdate is not set, the responder is indicating that newer
667 // revocation information is available all the time."
668 //
669 // This test is just to verify that we correctly handle such responses.
670 QDECLARE_CHAIN(subjectChain, "ss1.crt");
671 QDECLARE_CHAIN(responderChain, "ss1.crt");
672 QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
673
674 OcspServer server(subjectChain, privateKey);
675 OcspTimeStamp openRange(-1000, 0);
676 openRange.nextUpdate.clear();
677 const OcspResponder responder(openRange, subjectChain, responderChain, privateKey);
678 const QByteArray response(responder.buildResponse(OCSP_RESPONSE_STATUS_SUCCESSFUL,
679 V_OCSP_CERTSTATUS_GOOD));
680 QVERIFY(response.size());
681 server.configureResponse(responseDer: response);
682 QVERIFY(server.listen());
683 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
684
685 QSslSocket clientSocket;
686 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
687 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
688 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
689
690 QVERIFY_HANDSHAKE_WITHOUT_ERRORS(clientSocket);
691}
692
693void tst_QOcsp::wrongCertificateInResponse_data()
694{
695 QTest::addColumn<QLatin1String>(name: "respChainName");
696 QTest::addColumn<QLatin1String>(name: "respKeyName");
697 QTest::addColumn<QLatin1String>(name: "wrongChainName");
698
699 QTest::addRow(format: "same-CA-wrong-subject") << QLatin1String("ca1.crt") << QLatin1String("ca1.key")
700 << QLatin1String("alice.crt");
701 QTest::addRow(format: "wrong-CA-same-subject") << QLatin1String("ss1.crt") << QLatin1String("ss1-private.key")
702 << QLatin1String("alice.crt");
703 QTest::addRow(format: "wrong-CA-wrong-subject") << QLatin1String("ss1.crt") << QLatin1String("ss1-private.key")
704 << QLatin1String("ss1.crt");
705}
706
707void tst_QOcsp::wrongCertificateInResponse()
708{
709 QFETCH(const QLatin1String, respChainName);
710 QFETCH(const QLatin1String, respKeyName);
711 QFETCH(const QLatin1String, wrongChainName);
712 // In this test, the server will send a valid response (correctly signed
713 // by a trusted key/cert) but for a wrong certificate (not the one the
714 // server presented to the client in the server's 'Certificate' message).
715 const QSslError::SslError expectedError = QSslError::OcspResponseCertIdUnknown;
716
717 QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
718 QDECLARE_PRIVATE_KEY(subjectKey, "infbob.key");
719 QDECLARE_CHAIN(responderChain, respChainName);
720 QDECLARE_PRIVATE_KEY(responderKey, respKeyName);
721
722 QDECLARE_CHAIN(wrongChain, wrongChainName);
723
724 OcspServer server(subjectToChain(chain: subjectChain), subjectKey);
725 const QByteArray wrongResponse(goodResponse(subject: wrongChain, responder: responderChain, privateKey: responderKey));
726 QVERIFY(wrongResponse.size());
727 server.configureResponse(responseDer: wrongResponse);
728 QVERIFY(server.listen());
729 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
730
731 QSslSocket clientSocket;
732 setupOcspClient(clientSocket, trustedCAs: issuerToChain(chain: subjectChain), peerName: server.peerVerifyName());
733 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
734 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
735
736 QVERIFY(!clientSocket.isEncrypted());
737 QVERIFY(containsError(clientSocket.sslHandshakeErrors(), expectedError));
738}
739
740void tst_QOcsp::untrustedResponder()
741{
742 const QSslError::SslError expectedError = QSslError::OcspResponseCannotBeTrusted;
743
744 QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
745 QDECLARE_PRIVATE_KEY(subjectKey, "infbob.key");
746 QDECLARE_CHAIN(responderChain, "ca1.crt");
747 QDECLARE_PRIVATE_KEY(responderKey, "ca1.key");
748
749 OcspServer server(subjectChain, subjectKey);
750 const QByteArray response(goodResponse(subject: subjectToChain(chain: subjectChain), responder: responderChain, privateKey: responderKey));
751 QVERIFY(response.size());
752 server.configureResponse(responseDer: response);
753 QVERIFY(server.listen());
754 connect(sender: &server, signal: &OcspServer::internalServerError, receiver: &loop, slot: exitLoopSlot);
755
756 QSslSocket clientSocket;
757 setupOcspClient(clientSocket, trustedCAs: {}, peerName: server.peerVerifyName());
758 clientSocket.connectToHostEncrypted(hostName: server.hostName(), port: server.serverPort());
759 loop.enterLoopMSecs(ms: handshakeTimeoutMS);
760
761 QVERIFY(!clientSocket.isEncrypted());
762 QVERIFY(containsError(clientSocket.sslHandshakeErrors(), expectedError));
763}
764
765void tst_QOcsp::setupOcspClient(QSslSocket &clientSocket, const CertificateChain &caCerts, const QString &name)
766{
767 QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
768 clientConfig.setOcspStaplingEnabled(true);
769
770 if (caCerts.size()) {
771 auto roots = clientConfig.caCertificates();
772 roots.append(t: caCerts);
773 clientConfig.setCaCertificates(roots);
774 }
775
776 clientSocket.setSslConfiguration(clientConfig);
777 clientSocket.setPeerVerifyName(name);
778
779 connect(sender: &clientSocket, signal: &QAbstractSocket::errorOccurred, receiver: &loop, slot: exitLoopSlot);
780 connect(sender: &clientSocket, signal: tlsErrorsSignal, receiver: &loop, slot: exitLoopSlot);
781 connect(sender: &clientSocket, signal: &QSslSocket::encrypted, receiver: &loop, slot: exitLoopSlot);
782}
783
784bool tst_QOcsp::containsOcspErrors(const QList<QSslError> &errorsFound) const
785{
786 for (auto code : ocspErrorCodes) {
787 if (containsError(errors: errorsFound, code))
788 return true;
789 }
790 return false;
791}
792
793bool tst_QOcsp::containsError(const QList<QSslError> &errors, QSslError::SslError code)
794{
795 const auto it = std::find_if(first: errors.begin(), last: errors.end(),
796 pred: [&code](const QSslError &other){return other.error() == code;});
797 return it != errors.end();
798}
799
800QByteArray tst_QOcsp::goodResponse(const CertificateChain &subject, const CertificateChain &responder,
801 const QSslKey &privateKey, long beforeNow, long afterNow)
802{
803 const OcspResponder builder(OcspTimeStamp(beforeNow, afterNow), subject, responder, privateKey);
804 return builder.buildResponse(OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD);
805}
806
807bool tst_QOcsp::loadPrivateKey(const QString &keyFileName, QSslKey &key)
808{
809 QFile keyFile(certDirPath + keyFileName);
810 if (!keyFile.open(flags: QIODevice::ReadOnly))
811 return false;
812 key = QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
813 return !key.isNull();
814}
815
816CertificateChain tst_QOcsp::issuerToChain(const CertificateChain &chain)
817{
818 // Here we presume that, if the chain isn't a single self-signed certificate, its second
819 // entry is the issuer.
820 const int length = chain.size();
821 Q_ASSERT(length > 0);
822 return CertificateChain() << chain[length > 1 ? 1 : 0];
823}
824
825CertificateChain tst_QOcsp::subjectToChain(const CertificateChain &chain)
826{
827 Q_ASSERT(chain.size());
828 return CertificateChain() << chain[0];
829}
830
831QT_END_NAMESPACE
832
833QTEST_MAIN(tst_QOcsp)
834
835#include "tst_qocsp.moc"
836

source code of qtbase/tests/auto/network/ssl/qocsp/tst_qocsp.cpp