Skip to content

Commit

Permalink
Support for DH and ECDH key exchange for QSslSocket servers
Browse files Browse the repository at this point in the history
Despite supporting DH and ECDH key exchange as a client, Qt did not provide
any default parameters which prevented them being used as a server. A
future change should allow the user to control the parameters used, but
these defaults should be okay for most users.

[ChangeLog][Important Behavior Changes] Support for DH and ECDH key exchange
cipher suites when acting as an SSL server has been made possible. This
change means the you can now implement servers that offer forward-secrecy
using Qt.

Task-number: QTBUG-20666
Change-Id: I469163900e4313da9d2d0c3e1e5e47ef46320b17
Reviewed-by: Daniel Molkentin <[email protected]>
Reviewed-by: Peter Hartmann <[email protected]>
  • Loading branch information
richmoore authored and The Qt Project committed Apr 9, 2014
1 parent 71de8c0 commit 814a1c7
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/network/ssl/qsslcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,53 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();

// Default DH params
// 1024-bit MODP Group with 160-bit Prime Order Subgroup
// From RFC 5114
static unsigned const char dh1024_p[]={
0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E,
0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6,
0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86,
0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0,
0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C,
0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70,
0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA,
0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0,
0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF,
0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08,
0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71
};

static unsigned const char dh1024_g[]={
0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42,
0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F,
0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E,
0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13,
0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F,
0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1,
0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08,
0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A,
0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59,
0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24,
0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5
};

static DH *get_dh1024()
{
DH *dh = q_DH_new();
if (!dh)
return 0;

dh->p = q_BN_bin2bn(dh1024_p, sizeof(dh1024_p), 0);
dh->g = q_BN_bin2bn(dh1024_g, sizeof(dh1024_g), 0);
if (!dh->p || !dh->g) {
q_DH_free(dh);
return 0;
}

return dh;
}

QSslContext::QSslContext()
: ctx(0),
pkey(0),
Expand Down Expand Up @@ -261,6 +308,18 @@ QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSsl
if (!configuration.sessionTicket().isEmpty())
sslContext->setSessionASN1(configuration.sessionTicket());

// Set temp DH params
DH *dh = 0;
dh = get_dh1024();
q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
q_DH_free(dh);

// Set temp ECDH params
EC_KEY *ecdh = 0;
ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh);
q_EC_KEY_free(ecdh);

return sslContext;
}

Expand Down
10 changes: 10 additions & 0 deletions src/network/ssl/qsslsocket_openssl_symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return)
DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return 0, return)
DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)

#define RESOLVEFUNC(func) \
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
Expand Down Expand Up @@ -835,6 +840,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
RESOLVEFUNC(SSL_get0_next_proto_negotiated)
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(BN_bin2bn)
RESOLVEFUNC(EC_KEY_new_by_curve_name)
RESOLVEFUNC(EC_KEY_free)

symbolsResolved = true;
delete libs.first;
Expand Down
11 changes: 11 additions & 0 deletions src/network/ssl/qsslsocket_openssl_symbols_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,17 @@ int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);

// Diffie-Hellman support
DH *q_DH_new();
void q_DH_free(DH *dh);
BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
#define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh)

// EC Diffie-Hellman support
EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
void q_EC_KEY_free(EC_KEY *ecdh);
#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh)

#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
#ifdef SSLEAY_MACROS
Expand Down
67 changes: 67 additions & 0 deletions tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ private slots:
void resume();
void qtbug18498_peek();
void qtbug18498_peek2();
void dhServer();
void ecdhServer();
void setEmptyDefaultConfiguration(); // this test should be last

static void exitLoop()
Expand Down Expand Up @@ -1004,6 +1006,7 @@ class SslServer : public QTcpServer
QString m_keyFile;
QString m_certFile;
QString m_interFile;
QString ciphers;

protected:
void incomingConnection(qintptr socketDescriptor)
Expand Down Expand Up @@ -1037,6 +1040,10 @@ class SslServer : public QTcpServer
socket->setLocalCertificateChain(localCert + interCert);
}

if (!ciphers.isEmpty()) {
socket->setCiphers(ciphers);
}

QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
QVERIFY(!socket->peerAddress().isNull());
QVERIFY(socket->peerPort() != 0);
Expand Down Expand Up @@ -2665,6 +2672,66 @@ void tst_QSslSocket::qtbug18498_peek2()
QVERIFY(client->waitForDisconnected(5000));
}

void tst_QSslSocket::dhServer()
{
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}

QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;

SslServer server;
server.ciphers = QLatin1String("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA");
QVERIFY(server.listen());

QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));

QSslSocketPtr client(new QSslSocket);
socket = client.data();
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));

client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());

loop.exec();
QVERIFY(client->state() == QAbstractSocket::ConnectedState);
}

void tst_QSslSocket::ecdhServer()
{
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}

QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;

SslServer server;
server.ciphers = QLatin1String("ECDHE-RSA-AES128-SHA");
QVERIFY(server.listen());

QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));

QSslSocketPtr client(new QSslSocket);
socket = client.data();
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));

client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());

loop.exec();
QVERIFY(client->state() == QAbstractSocket::ConnectedState);
}

void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects
{
// used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265
Expand Down

0 comments on commit 814a1c7

Please sign in to comment.