2015-08-12 19:12:27 +01:00
/**
* Copyright 2015 Vineet Garg < albertvaka @ gmail . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License or ( at your option ) version 3 or any later version
* accepted by the membership of KDE e . V . ( or its successor approved
* by the membership of KDE e . V . ) , which shall act as a proxy
* defined in Section 14 of version 3 of the license .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "../core/backends/lan/server.h"
# include "../core/backends/lan/socketlinereader.h"
# include <QSslKey>
# include <QtCrypto>
# include <QTest>
# include <QTimer>
/*
* This class tests the behaviour of socket line reader when the connection if over ssl . Since SSL takes part below application layer ,
* working of SocketLineReader should be same .
*/
class TestSslSocketLineReader : public QObject
{
Q_OBJECT
public Q_SLOTS :
2018-03-04 19:48:51 +00:00
void newPacket ( ) ;
2015-08-12 19:12:27 +01:00
private Q_SLOTS :
void initTestCase ( ) ;
void init ( ) ;
void cleanup ( ) ;
void cleanupTestCase ( ) ;
void testTrustedDevice ( ) ;
void testUntrustedDevice ( ) ;
void testTrustedDeviceWithWrongCertificate ( ) ;
private :
const int PORT = 7894 ;
2017-09-03 20:39:44 +01:00
QTimer m_timer ;
QCA : : Initializer m_qcaInitializer ;
QEventLoop m_loop ;
2018-03-04 19:48:51 +00:00
QList < QByteArray > m_packets ;
2017-09-03 20:39:44 +01:00
Server * m_server ;
QSslSocket * m_clientSocket ;
SocketLineReader * m_reader ;
2015-08-12 19:12:27 +01:00
private :
void setSocketAttributes ( QSslSocket * socket , QString deviceName ) ;
} ;
void TestSslSocketLineReader : : initTestCase ( )
{
2017-09-03 20:39:44 +01:00
m_server = new Server ( this ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_server - > listen ( QHostAddress : : LocalHost , PORT ) , " Failed to create local tcp server " ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_timer . setInterval ( 10 * 1000 ) ; //Ten second is more enough for this test, just used this so that to break mLoop if stuck
m_timer . setSingleShot ( true ) ;
connect ( & m_timer , & QTimer : : timeout , & m_loop , & QEventLoop : : quit ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_timer . start ( ) ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : init ( )
{
2017-09-03 20:39:44 +01:00
m_clientSocket = new QSslSocket ( this ) ;
m_clientSocket - > connectToHost ( QHostAddress : : LocalHost , PORT ) ;
connect ( m_clientSocket , & QAbstractSocket : : connected , & m_loop , & QEventLoop : : quit ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_loop . exec ( ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_clientSocket - > isOpen ( ) , " Could not connect to local tcp server " ) ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : cleanup ( )
{
2017-09-03 20:39:44 +01:00
m_clientSocket - > disconnectFromHost ( ) ;
delete m_clientSocket ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : cleanupTestCase ( )
{
2017-09-03 20:39:44 +01:00
delete m_server ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : testTrustedDevice ( )
{
int maxAttemps = 5 ;
2017-09-03 20:39:44 +01:00
while ( ! m_server - > hasPendingConnections ( ) & & maxAttemps > 0 ) {
2015-08-12 19:12:27 +01:00
- - maxAttemps ;
QTest : : qSleep ( 1000 ) ;
}
2019-03-14 13:27:34 +00:00
QCOMPARE ( true , m_server - > hasPendingConnections ( ) ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QSslSocket * serverSocket = m_server - > nextPendingConnection ( ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket ! = 0 , " Null socket returned by server " ) ;
QVERIFY2 ( serverSocket - > isOpen ( ) , " Server socket already closed " ) ;
2016-11-26 14:38:08 +00:00
setSocketAttributes ( serverSocket , QStringLiteral ( " Test Server " ) ) ;
2017-09-03 20:39:44 +01:00
setSocketAttributes ( m_clientSocket , QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
2016-11-26 14:38:08 +00:00
serverSocket - > setPeerVerifyName ( QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > setPeerVerifyMode ( QSslSocket : : VerifyPeer ) ;
2017-09-03 20:39:44 +01:00
serverSocket - > addCaCertificate ( m_clientSocket - > localCertificate ( ) ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_clientSocket - > setPeerVerifyName ( QStringLiteral ( " Test Server " ) ) ;
m_clientSocket - > setPeerVerifyMode ( QSslSocket : : VerifyPeer ) ;
m_clientSocket - > addCaCertificate ( serverSocket - > localCertificate ( ) ) ;
2015-08-12 19:12:27 +01:00
2019-02-12 22:00:42 +00:00
int connected_sockets = 0 ;
auto connected_lambda = [ & ] ( ) {
connected_sockets + + ;
if ( connected_sockets > = 2 ) {
m_loop . quit ( ) ;
}
} ;
connect ( serverSocket , & QSslSocket : : encrypted , connected_lambda ) ;
connect ( m_clientSocket , & QSslSocket : : encrypted , connected_lambda ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > startServerEncryption ( ) ;
2017-09-03 20:39:44 +01:00
m_clientSocket - > startClientEncryption ( ) ;
2019-02-12 22:00:42 +00:00
m_loop . exec ( ) ; //Block until QEventLoop::quit gets called by the lambda
2015-08-12 19:12:27 +01:00
// Both client and server socket should be encrypted here and should have remote certificate because VerifyPeer is used
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_clientSocket - > isOpen ( ) , " Client socket already closed " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket - > isOpen ( ) , " Server socket already closed " ) ;
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_clientSocket - > isEncrypted ( ) , " Client is not encrypted " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket - > isEncrypted ( ) , " Server is not encrypted " ) ;
2017-09-03 20:39:44 +01:00
QVERIFY2 ( ! m_clientSocket - > peerCertificate ( ) . isNull ( ) , " Server certificate not received " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( ! serverSocket - > peerCertificate ( ) . isNull ( ) , " Client certificate not received " ) ;
QList < QByteArray > dataToSend ;
dataToSend < < " foobar \n " < < " barfoo \n " < < " foobar? \n " < < " \n " < < " barfoo! \n " < < " panda \n " ;
2018-11-25 22:58:24 +00:00
for ( const QByteArray & line : qAsConst ( dataToSend ) ) {
2017-09-03 20:39:44 +01:00
m_clientSocket - > write ( line ) ;
2015-08-12 19:12:27 +01:00
}
2017-09-03 20:39:44 +01:00
m_clientSocket - > flush ( ) ;
2015-08-12 19:12:27 +01:00
2018-03-04 19:48:51 +00:00
m_packets . clear ( ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_reader = new SocketLineReader ( serverSocket , this ) ;
2018-03-04 19:48:51 +00:00
connect ( m_reader , & SocketLineReader : : readyRead , this , & TestSslSocketLineReader : : newPacket ) ;
2017-09-03 20:39:44 +01:00
m_loop . exec ( ) ;
2015-08-12 19:12:27 +01:00
/* remove the empty line before compare */
dataToSend . removeOne ( " \n " ) ;
2018-03-04 19:48:51 +00:00
QCOMPARE ( m_packets . count ( ) , 5 ) ; //We expect 5 Packets
2015-08-12 19:12:27 +01:00
for ( int x = 0 ; x < 5 ; + + x ) {
2018-03-04 19:48:51 +00:00
QCOMPARE ( m_packets [ x ] , dataToSend [ x ] ) ;
2015-08-12 19:12:27 +01:00
}
2017-09-03 20:39:44 +01:00
delete m_reader ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : testUntrustedDevice ( )
{
int maxAttemps = 5 ;
2017-09-03 20:39:44 +01:00
while ( ! m_server - > hasPendingConnections ( ) & & maxAttemps > 0 ) {
2015-08-12 19:12:27 +01:00
- - maxAttemps ;
QTest : : qSleep ( 1000 ) ;
}
2019-03-14 13:27:34 +00:00
QCOMPARE ( true , m_server - > hasPendingConnections ( ) ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QSslSocket * serverSocket = m_server - > nextPendingConnection ( ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket ! = 0 , " Null socket returned by server " ) ;
QVERIFY2 ( serverSocket - > isOpen ( ) , " Server socket already closed " ) ;
2016-11-26 14:38:08 +00:00
setSocketAttributes ( serverSocket , QStringLiteral ( " Test Server " ) ) ;
2017-09-03 20:39:44 +01:00
setSocketAttributes ( m_clientSocket , QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
2016-11-26 14:38:08 +00:00
serverSocket - > setPeerVerifyName ( QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > setPeerVerifyMode ( QSslSocket : : QueryPeer ) ;
2017-09-03 20:39:44 +01:00
m_clientSocket - > setPeerVerifyName ( QStringLiteral ( " Test Server " ) ) ;
m_clientSocket - > setPeerVerifyMode ( QSslSocket : : QueryPeer ) ;
2015-08-12 19:12:27 +01:00
2019-02-12 22:00:42 +00:00
int connected_sockets = 0 ;
auto connected_lambda = [ & ] ( ) {
connected_sockets + + ;
if ( connected_sockets > = 2 ) {
m_loop . quit ( ) ;
}
} ;
connect ( serverSocket , & QSslSocket : : encrypted , connected_lambda ) ;
connect ( m_clientSocket , & QSslSocket : : encrypted , connected_lambda ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > startServerEncryption ( ) ;
2017-09-03 20:39:44 +01:00
m_clientSocket - > startClientEncryption ( ) ;
2019-02-12 22:00:42 +00:00
m_loop . exec ( ) ; //Block until QEventLoop::quit gets called by the lambda
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_clientSocket - > isOpen ( ) , " Client socket already closed " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket - > isOpen ( ) , " Server socket already closed " ) ;
2017-09-03 20:39:44 +01:00
QVERIFY2 ( m_clientSocket - > isEncrypted ( ) , " Client is not encrypted " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket - > isEncrypted ( ) , " Server is not encrypted " ) ;
2017-09-03 20:39:44 +01:00
QVERIFY2 ( ! m_clientSocket - > peerCertificate ( ) . isNull ( ) , " Server certificate not received " ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( ! serverSocket - > peerCertificate ( ) . isNull ( ) , " Client certificate not received " ) ;
QList < QByteArray > dataToSend ;
dataToSend < < " foobar \n " < < " barfoo \n " < < " foobar? \n " < < " \n " < < " barfoo! \n " < < " panda \n " ;
2018-11-25 22:58:24 +00:00
for ( const QByteArray & line : qAsConst ( dataToSend ) ) {
2017-09-03 20:39:44 +01:00
m_clientSocket - > write ( line ) ;
2015-08-12 19:12:27 +01:00
}
2017-09-03 20:39:44 +01:00
m_clientSocket - > flush ( ) ;
2015-08-12 19:12:27 +01:00
2018-03-04 19:48:51 +00:00
m_packets . clear ( ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
m_reader = new SocketLineReader ( serverSocket , this ) ;
2018-03-04 19:48:51 +00:00
connect ( m_reader , & SocketLineReader : : readyRead , this , & TestSslSocketLineReader : : newPacket ) ;
2017-09-03 20:39:44 +01:00
m_loop . exec ( ) ;
2015-08-12 19:12:27 +01:00
/* remove the empty line before compare */
dataToSend . removeOne ( " \n " ) ;
2018-03-04 19:48:51 +00:00
QCOMPARE ( m_packets . count ( ) , 5 ) ; //We expect 5 Packets
2015-08-12 19:12:27 +01:00
for ( int x = 0 ; x < 5 ; + + x ) {
2018-03-04 19:48:51 +00:00
QCOMPARE ( m_packets [ x ] , dataToSend [ x ] ) ;
2015-08-12 19:12:27 +01:00
}
2017-09-03 20:39:44 +01:00
delete m_reader ;
2015-08-12 19:12:27 +01:00
}
void TestSslSocketLineReader : : testTrustedDeviceWithWrongCertificate ( )
{
int maxAttemps = 5 ;
2017-09-03 20:39:44 +01:00
while ( ! m_server - > hasPendingConnections ( ) & & maxAttemps > 0 ) {
2015-08-12 19:12:27 +01:00
- - maxAttemps ;
QTest : : qSleep ( 1000 ) ;
}
2019-03-14 13:27:34 +00:00
QCOMPARE ( true , m_server - > hasPendingConnections ( ) ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QSslSocket * serverSocket = m_server - > nextPendingConnection ( ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( serverSocket ! = 0 , " Could not open a connection to the client " ) ;
2016-11-26 14:38:08 +00:00
setSocketAttributes ( serverSocket , QStringLiteral ( " Test Server " ) ) ;
2017-09-03 20:39:44 +01:00
setSocketAttributes ( m_clientSocket , QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
// Not adding other device certificate to list of CA certificate, and using VerifyPeer. This should lead to handshake failure
2016-11-26 14:38:08 +00:00
serverSocket - > setPeerVerifyName ( QStringLiteral ( " Test Client " ) ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > setPeerVerifyMode ( QSslSocket : : VerifyPeer ) ;
2017-09-03 20:39:44 +01:00
m_clientSocket - > setPeerVerifyName ( QStringLiteral ( " Test Server " ) ) ;
m_clientSocket - > setPeerVerifyMode ( QSslSocket : : VerifyPeer ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
connect ( serverSocket , & QSslSocket : : encrypted , & m_loop , & QEventLoop : : quit ) ; // Encrypted signal should never be emitted
connect ( m_clientSocket , & QSslSocket : : encrypted , & m_loop , & QEventLoop : : quit ) ; // Encrypted signal should never be emitted
connect ( serverSocket , & QAbstractSocket : : disconnected , & m_loop , & QEventLoop : : quit ) ;
connect ( m_clientSocket , & QAbstractSocket : : disconnected , & m_loop , & QEventLoop : : quit ) ;
2015-08-12 19:12:27 +01:00
serverSocket - > startServerEncryption ( ) ;
2017-09-03 20:39:44 +01:00
m_clientSocket - > startClientEncryption ( ) ;
m_loop . exec ( ) ;
2015-08-12 19:12:27 +01:00
QVERIFY2 ( ! serverSocket - > isEncrypted ( ) , " Server is encrypted, it should not " ) ;
2017-09-03 20:39:44 +01:00
QVERIFY2 ( ! m_clientSocket - > isEncrypted ( ) , " lient is encrypted, it should now " ) ;
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
if ( serverSocket - > state ( ) ! = QAbstractSocket : : UnconnectedState ) m_loop . exec ( ) ; // Wait until serverSocket is disconnected, It should be in disconnected state
if ( m_clientSocket - > state ( ) ! = QAbstractSocket : : UnconnectedState ) m_loop . exec ( ) ; // Wait until mClientSocket is disconnected, It should be in disconnected state
2015-08-12 19:12:27 +01:00
2017-09-03 20:39:44 +01:00
QCOMPARE ( ( int ) m_clientSocket - > state ( ) , 0 ) ;
2015-08-12 19:12:27 +01:00
QCOMPARE ( ( int ) serverSocket - > state ( ) , 0 ) ;
}
2018-03-04 19:48:51 +00:00
void TestSslSocketLineReader : : newPacket ( )
2015-08-12 19:12:27 +01:00
{
2017-09-03 20:39:44 +01:00
if ( ! m_reader - > bytesAvailable ( ) ) {
2015-08-12 19:12:27 +01:00
return ;
}
int maxLoops = 5 ;
2017-09-03 20:39:44 +01:00
while ( m_reader - > bytesAvailable ( ) > 0 & & maxLoops > 0 ) {
2015-08-12 19:12:27 +01:00
- - maxLoops ;
2018-03-04 19:48:51 +00:00
const QByteArray packet = m_reader - > readLine ( ) ;
if ( ! packet . isEmpty ( ) ) {
m_packets . append ( packet ) ;
2015-08-12 19:12:27 +01:00
}
2018-03-04 19:48:51 +00:00
if ( m_packets . count ( ) = = 5 ) {
2017-09-03 20:39:44 +01:00
m_loop . exit ( ) ;
2015-08-12 19:12:27 +01:00
}
}
}
2017-09-03 20:39:44 +01:00
void TestSslSocketLineReader : : setSocketAttributes ( QSslSocket * socket , QString deviceName ) {
2015-08-12 19:12:27 +01:00
QDateTime startTime = QDateTime : : currentDateTime ( ) ;
QDateTime endTime = startTime . addYears ( 10 ) ;
QCA : : CertificateInfo certificateInfo ;
certificateInfo . insert ( QCA : : CommonName , deviceName ) ;
2016-11-26 14:38:08 +00:00
certificateInfo . insert ( QCA : : Organization , QStringLiteral ( " KDE " ) ) ;
certificateInfo . insert ( QCA : : OrganizationalUnit , QStringLiteral ( " Kde connect " ) ) ;
2015-08-12 19:12:27 +01:00
QCA : : CertificateOptions certificateOptions ( QCA : : PKCS10 ) ;
certificateOptions . setSerialNumber ( 10 ) ;
certificateOptions . setInfo ( certificateInfo ) ;
certificateOptions . setValidityPeriod ( startTime , endTime ) ;
certificateOptions . setFormat ( QCA : : PKCS10 ) ;
QCA : : PrivateKey privKey = QCA : : KeyGenerator ( ) . createRSA ( 2048 ) ;
QSslCertificate certificate = QSslCertificate ( QCA : : Certificate ( certificateOptions , privKey ) . toPEM ( ) . toLatin1 ( ) ) ;
socket - > setPrivateKey ( QSslKey ( privKey . toPEM ( ) . toLatin1 ( ) , QSsl : : Rsa ) ) ;
socket - > setLocalCertificate ( certificate ) ;
}
QTEST_GUILESS_MAIN ( TestSslSocketLineReader )
# include "testsslsocketlinereader.moc"