diff options
Diffstat (limited to 'tests/auto/network/ssl/qdtls')
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-ca.crt | 20 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-ca.key | 27 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-client.crt | 19 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-client.key | 27 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-server.crt | 19 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/bogus-server.key | 27 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/fake-login.live.com.key | 15 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/fake-login.live.com.pem | 19 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/fluke.cert | 75 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/fluke.key | 15 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/ss-srv-cert.pem | 18 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/certs/ss-srv-key.pem | 18 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/qdtls.pro | 16 | ||||
-rw-r--r-- | tests/auto/network/ssl/qdtls/tst_qdtls.cpp | 1324 |
14 files changed, 1639 insertions, 0 deletions
diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-ca.crt b/tests/auto/network/ssl/qdtls/certs/bogus-ca.crt new file mode 100644 index 0000000000..cf5893e98d --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgIJAJBdFtmKuuELMA0GCSqGSIb3DQEBCwUAMC8xGjAYBgNV +BAoMEUJvZ3VzIENvcnBvcmF0aW9uMREwDwYDVQQDDAhCb2d1cyBDQTAgFw0xNTAx +MzAxNzM0NDdaGA8yMTE1MDEwNjE3MzQ0N1owLzEaMBgGA1UECgwRQm9ndXMgQ29y +cG9yYXRpb24xETAPBgNVBAMMCEJvZ3VzIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnXt/X69lmfvWampP88f20yNs1VZroG9VjdR4GaJM6pbWu5Wn +SYBfS81osnHC7dTW2FvKZUGnz7KX+ImkbE2qUvj6yTeFu6ILj3o+8ws7A4iOTkiH +84CHb6T/HxWO5fW6mS5v+tvPDp3rQ7JpPVYvoh7dSv8X1+JCdDmkepRveN6Pzo47 +9VFVC0oscc5I4Y0wPwnaXZ4X26vmRfbhqtoKL57lz1lJ0R6bvLC9mf4DGFPx7WXQ +eOtlKX2dtuKj+Cl3vyHff6gHNMKM0bq3KfsT+vDO6eIs/ayqVRdd0XBIMj+bZYd9 +7QI/+3XTNR3TwTisrjo71XZtHdA1DkcMaSGoJwIDAQABo1AwTjAdBgNVHQ4EFgQU +xVZK4BIjBgmluCLIespCbne4BIUwHwYDVR0jBBgwFoAUxVZK4BIjBgmluCLIespC +bne4BIUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAgZn6odHr2y1G +1OStblBdsXNxmsW7WzhLUYFUhSzBw9KS/O7uG2HAFLwJNM4sQHeuc0JjxqXG5n7s +mGbmWpUYt8+KJDRnUssmKwwg2u6Rqp+0I9leCk9KTtYpXX7d9wprSsgwjQKhTEeQ +fNImbNR6Br7GDO7Om2MnOALvZmp0KJgUFIH0J630LJTrsrTvwfX7wKhYb1wgud5N +SXdGjBuJxKK3Y0VBMsbqwI0y+wHIYE+qLzlFWNRHmKaYeGtg0T8CVK6XWUrLcjcr +rQINqW3rb1OlWF7YZ5dg7vXoZrza6YSQLWha6/FQMCaKtJHxIE1NBw0ZXK6txnkI +f4HXoPvSGg== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-ca.key b/tests/auto/network/ssl/qdtls/certs/bogus-ca.key new file mode 100644 index 0000000000..1c2db7932e --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnXt/X69lmfvWampP88f20yNs1VZroG9VjdR4GaJM6pbWu5Wn +SYBfS81osnHC7dTW2FvKZUGnz7KX+ImkbE2qUvj6yTeFu6ILj3o+8ws7A4iOTkiH +84CHb6T/HxWO5fW6mS5v+tvPDp3rQ7JpPVYvoh7dSv8X1+JCdDmkepRveN6Pzo47 +9VFVC0oscc5I4Y0wPwnaXZ4X26vmRfbhqtoKL57lz1lJ0R6bvLC9mf4DGFPx7WXQ +eOtlKX2dtuKj+Cl3vyHff6gHNMKM0bq3KfsT+vDO6eIs/ayqVRdd0XBIMj+bZYd9 +7QI/+3XTNR3TwTisrjo71XZtHdA1DkcMaSGoJwIDAQABAoIBAGKkKmJq4L8UyXca +ZD4UcHxL4i221e9GDVarURbtXDRMivAwivo1GHvIi93J+Ak0meYniJzoBQ7JlPsu +a/kSpK8YGS3UQ0YF+CvErI1b6XkLHefW8qEJTswVk1+LB1jvFBRCzA1bhVRogiaD +J/wtceSgZIhHRE4LAQj/2hCVzUTtV6Zr0GIJGjB7hdF9MHGlTwkPrkjvERlK/PTc +dVjyNbinYGJNA2i701u/atplH2eSBUresMhHu3AZUUXZKfFQ2m07FDBNAtsoYNnO +d17EXDaoQRDVWSP83GN4b/hpmngvHl1fuFBZ1ms375FNPQo/K33QBaUsLsqiIS/v +k3LBkeECgYEAyqv5dkgte9c2mxT5zUQySr1fDms4nwZTth8477jRnOZND1M9VoIv +1EjBfxq3y7gJVd34VWYeCxNBYwK8C45SDXtlU9X2hLeKWU6yfdegyxv950P5AahT +J80YtYSez+mTLPOC42GeTg7l01NXlTHmPpraIkdNniHc8bqyAEK9w+kCgYEAxuuO +Ln84GkAm1gr6gyFkOMVwVEfszKjRGIqp4BnSwM9bFgWvhyj4jpr+bpe4gQKQQE5q +E/GoxYOtdZ3yYupd2Ki0irGhhm3u0ywgmbomurOw46AInONWcHTU6kZY/dd8wfvW +8YcmFq/LNupwFOEw18mKaQXygMnUYci+uOSw0Y8CgYEAkcX0XjE4FdUL/6usqQme +KsfesR5J0YfZeism5rXGftXfI2C5w5lMEaJrGqL7A9pRTKOlVLdocIrfAvoaiy1I +s03H6e8Bqx/gsK+8DmujybNOgqMPXTPW68/HL/g9ykm0hCZ6RFYYaQiqIb/WRQdp +FiqHLxSeLVkp8+xWz30xxNECgYAA7P23Z64qKRxFKL3ruE8QGJMiQUdv2GVIuPR7 +b4NUlGJ3IsWjWmR1vXDrsNcR+qITOoox15ESgj9facHEBhUzue1FK/h1eLOA1ha8 +wGoHumhbVtZTbJdtZI3NHVCytbsF6Bci/p8FwgGvGr40yquAhZaYUIfFY6sSXW3N +zHqqLwKBgQCUGrePDhjjUZZNQya0TQZ95HL8OQB2e9bx8RwypYdC3pAZ6uDfl+Ne +IZoA8EoDHVbsxDXmLTGil/kyvmYBnzvkVz/yMyFm/7I0zXEOr8bTgqE5wJ8BMGSp +yil5jDoN28KL6D+HsDsWUEOvvHieDYP3cxfpZWiQuWIZ6gfDDVjIwQ== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-client.crt b/tests/auto/network/ssl/qdtls/certs/bogus-client.crt new file mode 100644 index 0000000000..c9d43ce662 --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-client.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMRowGAYDVQQKDBFCb2d1 +cyBDb3Jwb3JhdGlvbjERMA8GA1UEAwwIQm9ndXMgQ0EwIBcNMTUwMTMwMTczNTI0 +WhgPMjExNTAxMDYxNzM1MjRaMDMxGjAYBgNVBAoMEUJvZ3VzIENvcnBvcmF0aW9u +MRUwEwYDVQQDDAxCb2d1cyBDbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDa28y3b2qcrFTjr3GIgjx78qlbRZomBt/A//ZW5qx00+QXT30bu8F0 +jCfHaBTDSnabP86856C/kL1d6oRtc7jmaxNoj39uRh3NcV3VmFEiLI9XmJ0gOIBN +vMQ0voi4gvRBzjFMnVOFML8FePV4OUX1QUZK4eAvZCsDhaJv1cCEERsfcttv7X31 +CT3+a3geZsb0cMDqicq/uaX2IONhqoNYwGlmgF+bWICIxJmEnaK3e/LnKKpvvfTt +n2M0Fx0W4150HSZxQ9Iz6fQQ8oLNn3qNL5i9377XKpck2uxC39yt5WXK2d5m8xBF +5+qwMMqlEW4LoE/dTU9mJ1lZLwV7m7QJAgMBAAGjIDAeMAkGA1UdEwQCMAAwEQYJ +YIZIAYb4QgEBBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQBBeGwXbU/WRLkfxDoI +Js2nPqzpfEXAcrJhurHKlm/wMIHnHHhTM69O7yTl/VUdKIXPzC1bGkAiSBQo+51B +SJkyWo3vt47g8rqAnUs4oM+bPD2t1YkJVeGLu+Nfw5SHlc+HdojdAcpKtnCbqtrd +vnV4QyB70nxKXC3jmWVBu/jeim0RzUacO+lF9vRPqwnlDINopx8ZpEjaXxABtaQA +cVUosFGEPRjOYAbw9j4fK7J7EXh/124j81OfawkfaMMDt2EedmSdlhPy+Io7VaBo +ho+39cX/oO3Ek+C9v+4aGF7rgp3VyKOGtC5rIy+YiwjcI09pRVPuqEqXC6C4nQcS +SjjF +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-client.key b/tests/auto/network/ssl/qdtls/certs/bogus-client.key new file mode 100644 index 0000000000..f676af73d4 --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA2tvMt29qnKxU469xiII8e/KpW0WaJgbfwP/2VuasdNPkF099 +G7vBdIwnx2gUw0p2mz/OvOegv5C9XeqEbXO45msTaI9/bkYdzXFd1ZhRIiyPV5id +IDiATbzENL6IuIL0Qc4xTJ1ThTC/BXj1eDlF9UFGSuHgL2QrA4Wib9XAhBEbH3Lb +b+199Qk9/mt4HmbG9HDA6onKv7ml9iDjYaqDWMBpZoBfm1iAiMSZhJ2it3vy5yiq +b7307Z9jNBcdFuNedB0mcUPSM+n0EPKCzZ96jS+Yvd++1yqXJNrsQt/creVlytne +ZvMQRefqsDDKpRFuC6BP3U1PZidZWS8Fe5u0CQIDAQABAoIBAQDOzZlA0CgWiYTh +bLvEOQQ8Pw0msLs7KY2vCm7UqL3W2w4RtMvMM/tWTMWd2EyeSLOQeZe5ysmLmpJF +tz+RSSMzn4REbiwEoH6yzWfUWEx6FU8Rf6UheCJM0o04Jb59U0jJEbRl59eu6GPo +IOcaxkvDtv1b7tnvDiDTACiAsqNqZhs54QlqwpadSYe4QgK9KH0WxqBzLpXr8eEq +ZV1uuuNpaf+mitVaJhXHyVt7Od1yPfohbTYaXjko3xt3BcStt4tzRZkGQk2kjMWd +d53wqcFlc+zxSW9/ogLr+TCDttTEa1oV+JLpXLkV5J0/saf/LYw96r6f98XhLrd1 +5otsbQ+dAoGBAP0nCzd6otnuUsLX+dz0ed61zDzyTVBXLxuOOvDpuPItVUKPI8yZ +mwveIm97/4u50HGSWUgLR5v+ABfMVG/DqkEP50dDbIhQ2uBhkR5xVgSlZSiZ7S03 +1AErADaeViphKjfAuHraGgC6SRv8HBZadbYW+ZQRVTF6IRJmstiLNJIDAoGBAN1S +AYtYhH0tJSQxyL+sdeuPGhY5RDdlSeLRAStpoGjmaOC4Rc8uDsts2xuInkCcTW2y +nogoR5YxFvcly3vGL5kOzLuscLbueqkz/rbTlZPruqL7fMyPI7Y3YgGER5XNwPpE ++DlW1fu2aE42WUU49mkUNaT2WBtOLnbZKShAWKoDAoGAOGZfeF/JMnaHV8OYdmK9 +WCH2u8lb8j9KToBUn2HjA4mYCjkrx6SdR3qY/2+H0pB2YScy3vssXBOt3591XGUi +ZFZvt4/M+V3SNdVm6HplqKlUrUQF9GIQyKXU6VZDajO1nTBBqZU339ug+Cwl8dD7 +krLxrcxix6AnCBt7UwVIlBMCgYEAydQADogxgknKJiC0Vn86pg9BFeUxXWckIxDA +hUt0+lSsbcn993qkCUUC5zAGSRuAzLnoMnixF7k6nTW9Q+mu/GBvufH+dAQ0ndsJ +vMZlEJkXAYxf+dfLFF+bI5DzCxywkEqXJwsWZs6ofjK35BWXOKoyZXY1UOlSHBXb +n5ZWhOsCgYBRLqEjUehkZfqjZj8VClyPQ/6bAgtfjMRqpgsLgvqG9gBraDs4DXJr +K8Ac3+vCP8rqVwIUC0iu/5MFX75WJ7Go7wbAg7m91P9tmzSiLEm5H1toXJpla6nv +oLZW+jN9O1BaVow8f2qIEJMjHnDbuZnMPQlMGUD+g2tNgczfxT3MOA== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-server.crt b/tests/auto/network/ssl/qdtls/certs/bogus-server.crt new file mode 100644 index 0000000000..7e59f6128d --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMRowGAYDVQQKDBFCb2d1 +cyBDb3Jwb3JhdGlvbjERMA8GA1UEAwwIQm9ndXMgQ0EwIBcNMTUwMTMxMTc0MjI3 +WhgPMjExNTAxMDcxNzQyMjdaMDMxGjAYBgNVBAoMEUJvZ3VzIENvcnBvcmF0aW9u +MRUwEwYDVQQDDAxCb2d1cyBTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCv899JQxy/mpaQzscopmoKOkgbmwGwty1KiTpT09MU1+gtMHCfhmVp +nAiNIlQlDa+5mjhvyy1fSf+mgdjnvT5pdUAro633gfCv318EViwYsvA7/0ZumFqU +UyPWw4/2of/ZfJv2ewzMLoYEDKiLcXxInBsMlt5Lr7IBS8SNitDU+TAM7HLEIkMz +c0JpxY09H707tO8G3e93yfB5l8H+JdeEdPe+7PDfnsZZuMmaImiNYRByPTTuGvrN +I9I+OxcE4ZOMMNb3mzAoEFnyfHiCO2ehHl58y0a49ayAKJdP/FV3n2LtL/Zc5Ilq +b3VJgaShevrfIiItURjOAjDA9B95hYuZAgMBAAGjIDAeMAkGA1UdEwQCMAAwEQYJ +YIZIAYb4QgEBBAQDAgZAMA0GCSqGSIb3DQEBCwUAA4IBAQBhTqwD3HxamZGopq0K +r8KUdtliiPwo4GBFp0zg6VdSxo01WfpwFGOaeKNmV0JadtJ1DhcsdIUv2OvrxiWQ +1n0IGHULeazQnst1q1t/Vlup3IggKTGCLi8yd3acY8tr2wj9lGjWhsR+BcrCUTEB +BCpIsQiFA8+PTf/8SHuzMokDBP+j02fWCqwR749H4NDQgqrFsgzxLDA69XgvkNM3 ++HOsOR/QxeYIp54mqPnsNVhzV0JbpQpF4j9R5kMI/bsPmWH6W0GbSSyA07o8iVw7 +eqPbwHnIlHXzafvaGmF0QituAzU0nPgMc9OMxuoqacBSmSvmSdMmh///vr7O2KHO +7s+g +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/bogus-server.key b/tests/auto/network/ssl/qdtls/certs/bogus-server.key new file mode 100644 index 0000000000..bda8dae678 --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/bogus-server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAr/PfSUMcv5qWkM7HKKZqCjpIG5sBsLctSok6U9PTFNfoLTBw +n4ZlaZwIjSJUJQ2vuZo4b8stX0n/poHY570+aXVAK6Ot94Hwr99fBFYsGLLwO/9G +bphalFMj1sOP9qH/2Xyb9nsMzC6GBAyoi3F8SJwbDJbeS6+yAUvEjYrQ1PkwDOxy +xCJDM3NCacWNPR+9O7TvBt3vd8nweZfB/iXXhHT3vuzw357GWbjJmiJojWEQcj00 +7hr6zSPSPjsXBOGTjDDW95swKBBZ8nx4gjtnoR5efMtGuPWsgCiXT/xVd59i7S/2 +XOSJam91SYGkoXr63yIiLVEYzgIwwPQfeYWLmQIDAQABAoIBAHVRJJLjpZp3h2a8 +CHypIND69TM60hCywgcNoo9cEES4hL0ErEMhSCL3f5giyHoAOyeElZasoO8FFuk9 +cJNrUd7c59FxDECYKhQJ2n+4uSQqwxUt6xc4jESTfrTmpemrMD0h4ZehifHmH0M5 +8XMwUs7TDxIA0e0jE4vbqg05/m3RMHoeJ4W5K4dMxkJbjmyjjCr8aT8WP/KSTABS +YQPql0rs6WL5Q2s1I/i3I4qIS4CKk8Ym7O5/Wk1fxbCh2ABL2PhW8PZDzvsFYo2T +cwX0cc0EILBc3tOG11Iua6mK8y9Zz1BpUT02ZvGaPf9R6vI0Shk1yWbZ0NYLx0MH +Zu8HIYECgYEA5awzjNcnDYQY9f6C/0TNj54Z8I7UFmGJX7XhPVVMceNieUiLvrsH +Zmf4Q51PLM1iz0S2qGA/c7lngHDXwFe++MANIK7KNwL2LtPF/83mYgBUxBKJaNHD +4B/6CCitjSwAfMNBnE70zg0F9chqy+9p+fTEwUFW6Y4y9U5jO4kw5HECgYEAxB8+ +YYMUGeIt9TnMKrC2YK/o8jo+5ZEOpEIPwleeAIUMujVVonu3TX2nKos2MgaZg/F0 +OpvDlcQZqb4Em73ctf3ZgBYEs9tt2qdB5qGlg4Hs2wyfgKUPQGLX2RseUQCYsOWT +cPPKvYDTZ6yhW6gGBd5ufl5tnG93CsIpcNV1DakCgYEAwByZhi6V4Q1k36eDpcjE +dWRW6ExghVQS17dIb8hAyGbeAPs4wVKqbvN6y/vytVQbWapta0wO51rng51gKuh6 +upHSqUrrpLZafHLyBPYSxljmjpe+zqnfwUKeH2L/QL3UroeZAwlcZlqoaJ27D1j0 ++XrPdaOU8onagCyQfsVT21ECgYAafW3blezdIiO6/7eH/J5lqNz5+swMDe/AV/vw +8AyzXUU+0X1jmPpFSTePE4aaczHBFJfyYp+kVvxwZO4Say6olkUOe+resEDCS90m +3aaRgLcRTz8sDR9mPvOQq40Iu9/j5N5pX0R/HCtx0WtqCePmXwjloLOFcbjOhzM5 +vls1IQKBgEF8DEk8T4ycjwBXC3U7Duj9jPL815417BAHdGstLP1yNcI05ubN2T56 +ITbf625YS7OdtYfrf1/jBnUVXsJspsQqkOUB97M224CVWI+vJiv8jPX+KCnR7/Zh +A/7OrtZ6FCzLyBeu/2p1NHAttqSUqu9t6wCeeBcelnAUcrjfLmlw +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.key b/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.key new file mode 100644 index 0000000000..692a7bd85d --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDOtxdvMa0VHUQYG5q7Tsi1Jj4qKEJppyZEkmuRXOi0fDbd1SwE +bwHrLGMvDO6OMrYBbq3WDNrtnIfF9CvzUOEch+gjr4hEVQqecU5fb45Wor7yNel3 +/C/gxfbzuXHrsj/gUjNghL2i10+c2NW+hUo/sWO6OusaBT6d6s7ee+YBcQIDAQAB +AoGAb8cVhu0HuLkgjyCuJMbPRRUu3ED02Iin6sB6JhplQuNAD+grayJTmUVhRJnr +jTziqhedLHe7Em1oBaSo92MutfMpXvWiccSlbNygI61VgmrJpVB+qIN5H9cQc9ql +Zymc+nIPa1+i5rsrOzlpUytTh7AsbZ27QG4tQXR/kQejEiECQQD6BgTxBeT8D7x9 +DuukoBaSCkLwx7U7P1NXx15EI3lA1nO51t6UHfvk/jGPp8Sl4wv4alJ7AQxr5uQ/ +vC3kzA/1AkEA06gNu10se8pe3n8qL2RRt+FmVjHkQdD9Mm2Dx9oWCs2A4wOSOrlo +6/nKYF1CaQNYn9HgsNbHVEUpnICVO18qDQJBALEw/uOJ1+TDikPfBSWgxx4s45Ad +GNWqZXh6NNZ5hX9r/IwiOZAjR9fcRmeW8IjYRi2BvH6sGY+HDRAWXzgdXtkCQCma +dOiJTf8fLjqp4E7kdzOfuI/kyqstOze4Uxjrgz2oW1dEEnA8laUcumzqp+0gXUE8 +7d+UuCWWWrGKjMrYz9kCQQDh5E5+b6Djn082Jo6gvyuXWC5eXju6IdmihlJ2SMzD +s2y3IDjOUtTeQQRDymLneteMz0ha79KeUp6VnAvZCOVe +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.pem b/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.pem new file mode 100644 index 0000000000..429f95187c --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/fake-login.live.com.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIRALC3Ez7Qlvm1b66RyHS9OsAwDQYJKoZIhvcNAQEFBQAw +XjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMObG9naW4ubGl2ZS5jb20w +HhcNMTEwMzI1MTMyODUwWhcNMTEwNDI0MTMyODUwWjBeMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRcwFQYDVQQDEw5sb2dpbi5saXZlLmNvbTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAzrcXbzGtFR1EGBuau07ItSY+KihCaacmRJJrkVzotHw23dUs +BG8B6yxjLwzujjK2AW6t1gza7ZyHxfQr81DhHIfoI6+IRFUKnnFOX2+OVqK+8jXp +d/wv4MX287lx67I/4FIzYIS9otdPnNjVvoVKP7FjujrrGgU+nerO3nvmAXECAwEA +AaOByzCByDAdBgNVHQ4EFgQUpSOEcmtkQITvBdM2IDfcXnJ0FCAwgZgGA1UdIwSB +kDCBjYAUpSOEcmtkQITvBdM2IDfcXnJ0FCChYqRgMF4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxFzAVBgNVBAMTDmxvZ2luLmxpdmUuY29tghEAsLcTPtCW+bVvrpHIdL06 +wDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAD+2HT4GSHHKCdbl9VkX +zsl+D+drMm2b0ksxz9SgPihP7aW50EEIJDEEihNMTa27mhpeOXHc/sLqDi4ECUao +/0Ns/5uoVuAIrAKCydmtPsonVFh9XWjyrfUzPOHAc9p2bmJ1i9a3kTsLB6jlrVDO +VufGzsowHlHZ0TtKf5omojU5 +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/fluke.cert b/tests/auto/network/ssl/qdtls/certs/fluke.cert new file mode 100644 index 0000000000..ace4e4f0eb --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/fluke.cert @@ -0,0 +1,75 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + Validity + Not Before: Dec 4 01:10:32 2007 GMT + Not After : Apr 21 01:10:32 2035 GMT + Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1: + 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11: + 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3: + d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05: + aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45: + 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91: + 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91: + 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c: + 3b:f6:45:f3:27:6a:9b:94:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC + X509v3 Authority Key Identifier: + DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + serial:8E:A8:B4:E8:91:B7:54:2E + + Signature Algorithm: sha1WithRSAEncryption + 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4: + a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35: + 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1: + a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1: + a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3: + 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93: + ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42: + c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15: + 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34: + 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3: + b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34: + 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35: + 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d: + c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06: + ec:6a:f2:c3 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x +DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs +dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 +cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe +Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE +ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN +b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY +SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd +AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM +IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv +Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV +BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB +U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u +bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR +t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ +AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp +nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 ++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN +XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx +kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/fluke.key b/tests/auto/network/ssl/qdtls/certs/fluke.key new file mode 100644 index 0000000000..9d1664d609 --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/fluke.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ +VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 +CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB +AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz +/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri +KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s +1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 +VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE +oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW +A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub +K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c +VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC +AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/certs/ss-srv-cert.pem b/tests/auto/network/ssl/qdtls/certs/ss-srv-cert.pem new file mode 100644 index 0000000000..2c3d2e180d --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/ss-srv-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5TCCAk6gAwIBAgIJAP0E+KApnERsMA0GCSqGSIb3DQEBCwUAMIGJMQswCQYD +VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzELMAkGA1UECgwC +UXQxHzAdBgNVBAsMFlImRCAoQ29yZSBhbmQgTmV0d29yaykxEDAOBgNVBAMMB2Jv +Yi5vcmcxHDAaBgkqhkiG9w0BCQEWDWJvYkBnbWFpbC5jb20wHhcNMTgwNDI2MDgw +NDAxWhcNMjgwNDIzMDgwNDAxWjCBiTELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9z +bG8xDTALBgNVBAcMBE9zbG8xCzAJBgNVBAoMAlF0MR8wHQYDVQQLDBZSJkQgKENv +cmUgYW5kIE5ldHdvcmspMRAwDgYDVQQDDAdib2Iub3JnMRwwGgYJKoZIhvcNAQkB +Fg1ib2JAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWSh+1 +Xp5l3MgpzfTtiiuT4jMh8uQug/N7fyZlo+obMarT51dYWt1L5yFYyPw92FCtWgTi +rWFUh6Z8O/wIRkLRd/meKAqQsqRMnEVt4WSE9fA41XecWw1FJl2Ehwnl2C3Nj3GF +XonG+4Wg5EzH7JGEUUQIGQnuUTj06BkHLq0R8QIDAQABo1MwUTAdBgNVHQ4EFgQU +TRydf45RdKVcjhzNUYgfNq4f/W8wHwYDVR0jBBgwFoAUTRydf45RdKVcjhzNUYgf +Nq4f/W8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAsn1aZTcZU +4qD+ciziSYFM0Qk1GTCHtWf5nufB35AyuUqOK3h6QgRHovvNm+IMJIPMlnnwj5gF +G8UZ6mCYBUQPBuofZz8+XEL+N0QtvzUXA2/jVKn2TkcSvFXV90juC9KfhGhM93kQ +1R3qNPgxkuQqteffCbUyWnugv3/axNCjsQ== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qdtls/certs/ss-srv-key.pem b/tests/auto/network/ssl/qdtls/certs/ss-srv-key.pem new file mode 100644 index 0000000000..c2d912bf4d --- /dev/null +++ b/tests/auto/network/ssl/qdtls/certs/ss-srv-key.pem @@ -0,0 +1,18 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIC1DBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIBoCUfXYbG50CAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECHsb5ejBIDukBIICgFiMs6LbW0L4 +fxGDVNQWEbWf+1h4HJZsetWVqX5kh+9R3yfvK6vMVI5EQZ3mm8t5NDIKRFON6dN/ +uKGvZA+4vR6Vc5yeOOzL6sC8NsIU6eJOzkxXdyBahBHORm51dJCRtCdsoaWnx00I +jEAIvdv/dWh+kx9iMF6cZeOvIXbdVPuadjeh62cjPU/zyZSKCFd59zqWRMvIMIIM +kUOy2In5dVBb8/W9Zz0S8OG7KRJ+KdxxR5ev324L70XtRbb/tDVnGuMz+K83xbQC +ySJgIEvaz9lmhFeWiJ9HPGqcYtMAUUk4XgF5mcQDete8uCndDnxCy3uCyNxWtIm3 +dXRYzWZh+nbsbjYQWT3Lo3z3zkchB8vNDBfVcp6m0nx7spaUFlptrXujYKU9VQTK +2vAMhT9uUstLaHm+TEI1SuDBeugbvxy1DNI5lEQ3SG50L8r0m/OuQkV9zPVreHLX +nJdx7POS69WW+yac2SJX9mMSXICksLvaSGxSnFf1hMyozIp3xM4jGsxV+ckGw2I/ +CNUW2QHMuJ2/AvZo8cJY5iwbRgCaewAKkUiPZbGMDLeRapm9nrRFyTkLGyjI+4wG +wByzJ8ZZ2IlnZexXbcs0o8qoFmUxgA2R5Q3AvDYAg9/XGWtRcbIOhly4gRv4B/OF +pWfqYrCK6ZbTNSNZNifMDJfjx5T9qSnnLbAZRjuVwOgvqWTcBZpzpn97YS7XnsrU +5qMOiaP+VgpWeO2Fcf5zq9CJaBLO8sBUWWJW7mPgrOpxbBOio6x1GbRMT2niOiPN +wSXWPfw5Kp/P43ORJEttudEGJYpIMAzZ88hmF2j8n3hGC9FHAw5RGaV+3vsonsZq +iwE2rYxs3RI= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qdtls/qdtls.pro b/tests/auto/network/ssl/qdtls/qdtls.pro new file mode 100644 index 0000000000..19e13a965c --- /dev/null +++ b/tests/auto/network/ssl/qdtls/qdtls.pro @@ -0,0 +1,16 @@ +CONFIG += testcase + +SOURCES += tst_qdtls.cpp +QT = core network-private testlib + +TARGET = tst_qdtls + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug + } else { + DESTDIR = release + } +} + +TESTDATA += certs diff --git a/tests/auto/network/ssl/qdtls/tst_qdtls.cpp b/tests/auto/network/ssl/qdtls/tst_qdtls.cpp new file mode 100644 index 0000000000..6a94eee389 --- /dev/null +++ b/tests/auto/network/ssl/qdtls/tst_qdtls.cpp @@ -0,0 +1,1324 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtNetwork/qsslpresharedkeyauthenticator.h> +#include <QtNetwork/qsslconfiguration.h> +#include <QtNetwork/qhostaddress.h> +#include <QtNetwork/qsslsocket.h> +#include <QtNetwork/qsslcipher.h> +#include <QtNetwork/qudpsocket.h> +#include <QtNetwork/qsslerror.h> +#include <QtNetwork/qsslkey.h> +#include <QtNetwork/qdtls.h> +#include <QtNetwork/qssl.h> + +#include <QtCore/qcryptographichash.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvector.h> +#include <QtCore/qstring.h> +#include <QtCore/qobject.h> + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace +{ + +bool dtlsErrorIsCleared(const QDtls &dtls) +{ + return dtls.dtlsError() == QDtlsError::NoError && dtls.dtlsErrorString().isEmpty(); +} + +using DtlsPtr = QScopedPointer<QDtls>; + +bool dtlsErrorIsCleared(DtlsPtr &dtls) +{ + return dtlsErrorIsCleared(*dtls); +} + +} // unnamed namespace + +#define QDTLS_VERIFY_NO_ERROR(obj) QVERIFY(dtlsErrorIsCleared(obj)) + +#define QDTLS_VERIFY_HANDSHAKE_SUCCESS(obj) \ + QVERIFY(obj->isConnectionEncrypted()); \ + QCOMPARE(obj->handshakeState(), QDtls::HandshakeComplete); \ + QDTLS_VERIFY_NO_ERROR(obj); \ + QCOMPARE(obj->peerVerificationErrors().size(), 0) + +class tst_QDtls : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void init(); + +private slots: + // Tests: + void construction_data(); + void construction(); + void configuration_data(); + void configuration(); + void invalidConfiguration(); + void setPeer_data(); + void setPeer(); + void handshake_data(); + void handshake(); + void handshakeWithRetransmission(); + void sessionCipher(); + void cipherPreferences_data(); + void cipherPreferences(); + void protocolVersionMatching_data(); + void protocolVersionMatching(); + void verificationErrors_data(); + void verificationErrors(); + void presetExpectedErrors_data(); + void presetExpectedErrors(); + void verifyServerCertificate_data(); + void verifyServerCertificate(); + void verifyClientCertificate_data(); + void verifyClientCertificate(); + void blacklistedCerificate(); + void readWriteEncrypted_data(); + void readWriteEncrypted(); + void datagramFragmentation(); + +protected slots: + void handshakeReadyRead(); + void encryptedReadyRead(); + void pskRequested(QSslPreSharedKeyAuthenticator *auth); + void handleHandshakeTimeout(); + +private: + void clientServerData(); + void connectHandshakeReadingSlots(); + void connectEncryptedReadingSlots(); + bool verificationErrorDetected(QSslError::SslError code) const; + + static QHostAddress toNonAny(const QHostAddress &addr); + + QUdpSocket serverSocket; + QHostAddress serverAddress; + quint16 serverPort = 0; + QSslConfiguration defaultServerConfig; + QSslCertificate selfSignedCert; + QString hostName; + QSslKey serverKeySS; + bool serverDropDgram = false; + const QByteArray serverExpectedPlainText = "Hello W ... hmm, I mean DTLS server!"; + QByteArray serverReceivedPlainText; + + QUdpSocket clientSocket; + QHostAddress clientAddress; + quint16 clientPort = 0; + bool clientDropDgram = false; + const QByteArray clientExpectedPlainText = "Hello DTLS client."; + QByteArray clientReceivedPlainText; + + DtlsPtr serverCrypto; + DtlsPtr clientCrypto; + + QTestEventLoop testLoop; + const int handshakeTimeoutMS = 5000; + const int dataExchangeTimeoutMS = 1000; + + const QByteArray presharedKey = "DEADBEEFDEADBEEF"; + QString certDirPath; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QSsl::SslProtocol) +Q_DECLARE_METATYPE(QSslSocket::SslMode) +Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode) +Q_DECLARE_METATYPE(QList<QSslCertificate>) +Q_DECLARE_METATYPE(QSslKey) +Q_DECLARE_METATYPE(QVector<QSslError>) + +QT_BEGIN_NAMESPACE + +void tst_QDtls::initTestCase() +{ + certDirPath = QFileInfo(QFINDTESTDATA("certs")).absolutePath(); + QVERIFY(certDirPath.size() > 0); + certDirPath += QDir::separator() + QStringLiteral("certs") + QDir::separator(); + + QVERIFY(QSslSocket::supportsSsl()); + + QFile keyFile(certDirPath + QStringLiteral("ss-srv-key.pem")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + serverKeySS = QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "foobar"); + QVERIFY(!serverKeySS.isNull()); + + QList<QSslCertificate> certificates = QSslCertificate::fromPath(certDirPath + QStringLiteral("ss-srv-cert.pem")); + QVERIFY(!certificates.isEmpty()); + QVERIFY(!certificates.first().isNull()); + selfSignedCert = certificates.first(); + + defaultServerConfig = QSslConfiguration::defaultDtlsConfiguration(); + defaultServerConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + defaultServerConfig.setDtlsCookieVerificationEnabled(false); + + hostName = QStringLiteral("bob.org"); +} + +void tst_QDtls::init() +{ + if (serverSocket.state() != QAbstractSocket::UnconnectedState) { + serverSocket.close(); + // disconnect signals/slots: + serverSocket.disconnect(); + } + + QVERIFY(serverSocket.bind()); + serverAddress = toNonAny(serverSocket.localAddress()); + serverPort = serverSocket.localPort(); + + if (clientSocket.localPort()) { + clientSocket.close(); + // disconnect signals/slots: + clientSocket.disconnect(); + } + + clientAddress = {}; + clientPort = 0; + + serverCrypto.reset(new QDtls(QSslSocket::SslServerMode)); + serverDropDgram = false; + serverReceivedPlainText.clear(); + + clientCrypto.reset(new QDtls(QSslSocket::SslClientMode)); + clientDropDgram = false; + clientReceivedPlainText.clear(); + + connect(clientCrypto.data(), &QDtls::handshakeTimeout, + this, &tst_QDtls::handleHandshakeTimeout); + connect(serverCrypto.data(), &QDtls::handshakeTimeout, + this, &tst_QDtls::handleHandshakeTimeout); +} + +void tst_QDtls::construction_data() +{ + clientServerData(); +} + +void tst_QDtls::construction() +{ + QFETCH(const QSslSocket::SslMode, mode); + + QDtls dtls(mode); + QCOMPARE(dtls.peerAddress(), QHostAddress()); + QCOMPARE(dtls.peerPort(), quint16()); + QCOMPARE(dtls.peerVerificationName(), QString()); + QCOMPARE(dtls.sslMode(), mode); + + QCOMPARE(dtls.mtuHint(), quint16()); + + const auto params = dtls.cookieGeneratorParameters(); + QVERIFY(params.secret.size() > 0); +#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + QCOMPARE(params.hash, QCryptographicHash::Sha1); +#else + QCOMPARE(params.hash, QCryptographicHash::Sha256); +#endif + + QCOMPARE(dtls.dtlsConfiguration(), QSslConfiguration::defaultDtlsConfiguration()); + + QCOMPARE(dtls.handshakeState(), QDtls::HandshakeNotStarted); + QCOMPARE(dtls.isConnectionEncrypted(), false); + QCOMPARE(dtls.sessionCipher(), QSslCipher()); + QCOMPARE(dtls.sessionProtocol(), QSsl::UnknownProtocol); + + QCOMPARE(dtls.dtlsError(), QDtlsError::NoError); + QCOMPARE(dtls.dtlsErrorString(), QString()); + QCOMPARE(dtls.peerVerificationErrors().size(), 0); +} + +void tst_QDtls::configuration_data() +{ + clientServerData(); +} + +void tst_QDtls::configuration() +{ + // There is a proper auto-test for QSslConfiguration in our TLS test suite, + // here we only test several DTLS-related details. + auto config = QSslConfiguration::defaultDtlsConfiguration(); + QCOMPARE(config.protocol(), QSsl::DtlsV1_2OrLater); + + const QList<QSslCipher> ciphers = config.ciphers(); + QVERIFY(ciphers.size() > 0); + for (const auto &cipher : ciphers) + QVERIFY(cipher.usedBits() >= 128); + + QCOMPARE(config.dtlsCookieVerificationEnabled(), true); + + QFETCH(const QSslSocket::SslMode, mode); + QDtls dtls(mode); + QCOMPARE(dtls.dtlsConfiguration(), config); + config.setProtocol(QSsl::DtlsV1_0OrLater); + config.setDtlsCookieVerificationEnabled(false); + QCOMPARE(config.dtlsCookieVerificationEnabled(), false); + + QVERIFY(dtls.setDtlsConfiguration(config)); + QDTLS_VERIFY_NO_ERROR(dtls); + QCOMPARE(dtls.dtlsConfiguration(), config); + + if (mode == QSslSocket::SslClientMode) { + // Testing a DTLS server would be more complicated, we'd need a DTLS + // client sending ClientHello(s), running an event loop etc. - way too + // much dancing for a simple setter/getter test. + QVERIFY(dtls.setPeer(serverAddress, serverPort)); + QDTLS_VERIFY_NO_ERROR(dtls); + + QUdpSocket clientSocket; + QVERIFY(dtls.doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(dtls); + QCOMPARE(dtls.handshakeState(), QDtls::HandshakeInProgress); + // As soon as handshake started, it's not allowed to change configuration: + QVERIFY(!dtls.setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration())); + QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidOperation); + QCOMPARE(dtls.dtlsConfiguration(), config); + } +} + +void tst_QDtls::invalidConfiguration() +{ + QUdpSocket socket; + QDtls crypto(QSslSocket::SslClientMode); + QVERIFY(crypto.setPeer(serverAddress, serverPort)); + // Note: not defaultDtlsConfiguration(), so the protocol is TLS (without D): + QVERIFY(crypto.setDtlsConfiguration(QSslConfiguration::defaultConfiguration())); + QDTLS_VERIFY_NO_ERROR(crypto); + QCOMPARE(crypto.dtlsConfiguration(), QSslConfiguration::defaultConfiguration()); + // Try to start the handshake: + QCOMPARE(crypto.doHandshake(&socket), false); + QCOMPARE(crypto.dtlsError(), QDtlsError::TlsInitializationError); +} + +void tst_QDtls::setPeer_data() +{ + clientServerData(); +} + +void tst_QDtls::setPeer() +{ + static const QHostAddress invalid[] = {QHostAddress(), + QHostAddress(QHostAddress::Broadcast), + QHostAddress(QStringLiteral("224.0.0.0"))}; + static const QString peerName = QStringLiteral("does not matter actually"); + + QFETCH(const QSslSocket::SslMode, mode); + QDtls dtls(mode); + + for (const auto &addr : invalid) { + QCOMPARE(dtls.setPeer(addr, 100, peerName), false); + QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidInputParameters); + QCOMPARE(dtls.peerAddress(), QHostAddress()); + QCOMPARE(dtls.peerPort(), quint16()); + QCOMPARE(dtls.peerVerificationName(), QString()); + } + + QVERIFY(dtls.setPeer(serverAddress, serverPort, peerName)); + QDTLS_VERIFY_NO_ERROR(dtls); + QCOMPARE(dtls.peerAddress(), serverAddress); + QCOMPARE(dtls.peerPort(), serverPort); + QCOMPARE(dtls.peerVerificationName(), peerName); + + if (mode == QSslSocket::SslClientMode) { + // We test for client mode only, for server mode we'd have to run event + // loop etc. too much work for a simple setter/getter test. + QUdpSocket clientSocket; + QVERIFY(dtls.doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(dtls); + QCOMPARE(dtls.handshakeState(), QDtls::HandshakeInProgress); + QCOMPARE(dtls.setPeer(serverAddress, serverPort), false); + QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidOperation); + } +} + +void tst_QDtls::handshake_data() +{ + QTest::addColumn<bool>("withCertificate"); + + QTest::addRow("no-cert") << false; + QTest::addRow("with-cert") << true; +} + +void tst_QDtls::handshake() +{ + connectHandshakeReadingSlots(); + + QFETCH(const bool, withCertificate); + + auto serverConfig = defaultServerConfig; + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + + if (!withCertificate) { + connect(serverCrypto.data(), &QDtls::pskRequired, this, &tst_QDtls::pskRequested); + connect(clientCrypto.data(), &QDtls::pskRequired, this, &tst_QDtls::pskRequested); + clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + QVERIFY(clientConfig.peerCertificate().isNull()); + } else { + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setLocalCertificate(selfSignedCert); + clientConfig.setCaCertificates({selfSignedCert}); + } + + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + + // Some early checks before we run event loop. + // Remote was not set yet: + QVERIFY(!clientCrypto->doHandshake(&clientSocket)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + QVERIFY(!serverCrypto->doHandshake(&serverSocket, QByteArray("ClientHello"))); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); + + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); + + // Invalid socket: + QVERIFY(!clientCrypto->doHandshake(nullptr)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); + + // Now we are ready for handshake: + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + QVERIFY(!testLoop.timeout()); + + QVERIFY(serverCrypto->isConnectionEncrypted()); + QDTLS_VERIFY_NO_ERROR(serverCrypto); + QCOMPARE(serverCrypto->handshakeState(), QDtls::HandshakeComplete); + QCOMPARE(serverCrypto->peerVerificationErrors().size(), 0); + + QVERIFY(clientCrypto->isConnectionEncrypted()); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); + QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); + + if (withCertificate) { + const auto serverCert = clientCrypto->dtlsConfiguration().peerCertificate(); + QVERIFY(!serverCert.isNull()); + QCOMPARE(serverCert, selfSignedCert); + } + + // Already in 'HandshakeComplete' state/encrypted. + QVERIFY(!clientCrypto->doHandshake(&clientSocket)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + QVERIFY(!serverCrypto->doHandshake(&serverSocket, {"ServerHello"})); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); + // Cannot change a remote without calling shutdown first. + QVERIFY(!clientCrypto->setPeer(serverAddress, serverPort)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + QVERIFY(!serverCrypto->setPeer(clientAddress, clientPort)); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); +} + +void tst_QDtls::handshakeWithRetransmission() +{ + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setLocalCertificate(selfSignedCert); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setCaCertificates({selfSignedCert}); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); + + // Now we are ready for handshake: + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); + + serverDropDgram = true; + clientDropDgram = true; + // Every failed re-transmission doubles the next timeout. We don't want to + // slow down the test just to check the re-transmission ability, so we'll + // drop only the first 'ClientHello' and 'ServerHello' datagrams. The + // arithmetic is approximately this: the first ClientHello to be dropped - + // client will re-transmit in 1s., the first part of 'ServerHello' to be + // dropped, the client then will re-transmit after another 2 s. Thus it's ~3. + // We err on safe side and double our (already quite generous) 5s. + testLoop.enterLoopMSecs(handshakeTimeoutMS * 2); + + QVERIFY(!testLoop.timeout()); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); +} + +void tst_QDtls::sessionCipher() +{ + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setLocalCertificate(selfSignedCert); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setCaCertificates({selfSignedCert}); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + QVERIFY(!testLoop.timeout()); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + + const auto defaultDtlsConfig = QSslConfiguration::defaultDtlsConfiguration(); + + const auto clCipher = clientCrypto->sessionCipher(); + QVERIFY(!clCipher.isNull()); + QVERIFY(defaultDtlsConfig.ciphers().contains(clCipher)); + + const auto srvCipher = serverCrypto->sessionCipher(); + QVERIFY(!srvCipher.isNull()); + QVERIFY(defaultDtlsConfig.ciphers().contains(srvCipher)); + + QCOMPARE(clCipher, srvCipher); +} + +void tst_QDtls::cipherPreferences_data() +{ + QTest::addColumn<bool>("preferClient"); + + QTest::addRow("prefer-server") << true; + QTest::addRow("prefer-client") << false; +} + +void tst_QDtls::cipherPreferences() +{ + // This test is based on the similar case in tst_QSslSocket. We test it for QDtls + // because it's possible to set ciphers and corresponding ('server preferred') + // options via QSslConfiguration. + const QSslCipher aes128(QStringLiteral("AES128-SHA")); + const QSslCipher aes256(QStringLiteral("AES256-SHA")); + + auto serverConfig = defaultServerConfig; + const QList<QSslCipher> ciphers = serverConfig.ciphers(); + if (!ciphers.contains(aes128) || !ciphers.contains(aes256)) + QSKIP("The ciphers needed by this test were not found in the default DTLS configuration"); + + serverConfig.setCiphers({aes128, aes256}); + serverConfig.setLocalCertificate(selfSignedCert); + serverConfig.setPrivateKey(serverKeySS); + + QFETCH(const bool, preferClient); + if (preferClient) + serverConfig.setSslOption(QSsl::SslOptionDisableServerCipherPreference, true); + + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + QDTLS_VERIFY_NO_ERROR(serverCrypto); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + clientConfig.setCiphers({aes256, aes128}); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + + connectHandshakeReadingSlots(); + + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + QVERIFY(!testLoop.timeout()); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + + if (preferClient) { + QCOMPARE(clientCrypto->sessionCipher(), aes256); + QCOMPARE(serverCrypto->sessionCipher(), aes256); + } else { + QCOMPARE(clientCrypto->sessionCipher(), aes128); + QCOMPARE(serverCrypto->sessionCipher(), aes128); + } +} + +void tst_QDtls::protocolVersionMatching_data() +{ + QTest::addColumn<QSsl::SslProtocol>("serverProtocol"); + QTest::addColumn<QSsl::SslProtocol>("clientProtocol"); + QTest::addColumn<bool>("works"); + + QTest::addRow("DtlsV1_0 <-> DtlsV1_0") << QSsl::DtlsV1_0 << QSsl::DtlsV1_0 << true; + QTest::addRow("DtlsV1_0OrLater <-> DtlsV1_0") << QSsl::DtlsV1_0OrLater << QSsl::DtlsV1_0 << true; + QTest::addRow("DtlsV1_0 <-> DtlsV1_0OrLater") << QSsl::DtlsV1_0 << QSsl::DtlsV1_0OrLater << true; + QTest::addRow("DtlsV1_0OrLater <-> DtlsV1_0OrLater") << QSsl::DtlsV1_0OrLater << QSsl::DtlsV1_0OrLater << true; + + QTest::addRow("DtlsV1_2 <-> DtlsV1_2") << QSsl::DtlsV1_2 << QSsl::DtlsV1_2 << true; + QTest::addRow("DtlsV1_2OrLater <-> DtlsV1_2") << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_2 << true; + QTest::addRow("DtlsV1_2 <-> DtlsV1_2OrLater") << QSsl::DtlsV1_2 << QSsl::DtlsV1_2OrLater << true; + QTest::addRow("DtlsV1_2OrLater <-> DtlsV1_2OrLater") << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_2OrLater << true; + + QTest::addRow("DtlsV1_0 <-> DtlsV1_2") << QSsl::DtlsV1_0 << QSsl::DtlsV1_2 << false; + QTest::addRow("DtlsV1_0 <-> DtlsV1_2OrLater") << QSsl::DtlsV1_0 << QSsl::DtlsV1_2OrLater << false; + QTest::addRow("DtlsV1_2 <-> DtlsV1_0") << QSsl::DtlsV1_2 << QSsl::DtlsV1_0 << false; + QTest::addRow("DtlsV1_2OrLater <-> DtlsV1_0") << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_0 << false; +} + +void tst_QDtls::protocolVersionMatching() +{ + QFETCH(const QSsl::SslProtocol, serverProtocol); + QFETCH(const QSsl::SslProtocol, clientProtocol); + QFETCH(const bool, works); + + connectHandshakeReadingSlots(); + + connect(serverCrypto.data(), &QDtls::pskRequired, this, &tst_QDtls::pskRequested); + connect(clientCrypto.data(), &QDtls::pskRequired, this, &tst_QDtls::pskRequested); + + auto serverConfig = defaultServerConfig; + serverConfig.setProtocol(serverProtocol); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + clientConfig.setProtocol(clientProtocol); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + if (works) { + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + } else { + QCOMPARE(serverCrypto->isConnectionEncrypted(), false); + QVERIFY(serverCrypto->handshakeState() != QDtls::HandshakeComplete); + QCOMPARE(clientCrypto->isConnectionEncrypted(), false); + QVERIFY(clientCrypto->handshakeState() != QDtls::HandshakeComplete); + } +} + +void tst_QDtls::verificationErrors_data() +{ + QTest::addColumn<bool>("abortHandshake"); + + QTest::addRow("abort-handshake") << true; + QTest::addRow("ignore-errors") << false; +} + +void tst_QDtls::verificationErrors() +{ + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setLocalCertificate(selfSignedCert); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + // And our client already has the default DTLS configuration. + + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + // Now we are ready for handshake: + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + QVERIFY(!testLoop.timeout()); + QDTLS_VERIFY_NO_ERROR(serverCrypto); + + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); + QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); + QVERIFY(!clientCrypto->isConnectionEncrypted()); + + QVERIFY(verificationErrorDetected(QSslError::HostNameMismatch)); + QVERIFY(verificationErrorDetected(QSslError::SelfSignedCertificate)); + + const auto serverCert = clientCrypto->dtlsConfiguration().peerCertificate(); + QVERIFY(!serverCert.isNull()); + QCOMPARE(selfSignedCert, serverCert); + + QFETCH(const bool, abortHandshake); + + if (abortHandshake) { + QVERIFY(!clientCrypto->abortHandshake(nullptr)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); + QVERIFY(clientCrypto->abortHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QVERIFY(!clientCrypto->isConnectionEncrypted()); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeNotStarted); + QCOMPARE(clientCrypto->sessionCipher(), QSslCipher()); + QCOMPARE(clientCrypto->sessionProtocol(), QSsl::UnknownProtocol); + const auto config = clientCrypto->dtlsConfiguration(); + QVERIFY(config.peerCertificate().isNull()); + QCOMPARE(config.peerCertificateChain().size(), 0); + QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); + } else { + clientCrypto->ignoreVerificationErrors(clientCrypto->peerVerificationErrors()); + QVERIFY(!clientCrypto->resumeHandshake(nullptr)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); + QVERIFY(clientCrypto->resumeHandshake(&clientSocket)); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + QVERIFY(clientCrypto->isConnectionEncrypted()); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); + QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); + } +} + +void tst_QDtls::presetExpectedErrors_data() +{ + QTest::addColumn<QVector<QSslError>>("expectedTlsErrors"); + QTest::addColumn<bool>("works"); + + QVector<QSslError> expectedErrors{{QSslError::HostNameMismatch, selfSignedCert}}; + QTest::addRow("unexpected-self-signed") << expectedErrors << false; + expectedErrors.push_back({QSslError::SelfSignedCertificate, selfSignedCert}); + QTest::addRow("all-errors-ignored") << expectedErrors << true; +} + +void tst_QDtls::presetExpectedErrors() +{ + QFETCH(const QVector<QSslError>, expectedTlsErrors); + QFETCH(const bool, works); + + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setLocalCertificate(selfSignedCert); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + clientCrypto->ignoreVerificationErrors(expectedTlsErrors); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + QVERIFY(!testLoop.timeout()); + + if (works) { + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); + QVERIFY(clientCrypto->isConnectionEncrypted()); + } else { + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); + QVERIFY(!clientCrypto->isConnectionEncrypted()); + QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); + } +} + +void tst_QDtls::verifyServerCertificate_data() +{ + QTest::addColumn<QSslSocket::PeerVerifyMode>("verifyMode"); + QTest::addColumn<QList<QSslCertificate>>("serverCerts"); + QTest::addColumn<QSslKey>("serverKey"); + QTest::addColumn<QString>("peerName"); + QTest::addColumn<bool>("encrypted"); + + { + // A special case - null key (but with certificate): + const auto chain = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-server.crt")); + QCOMPARE(chain.size(), 1); + + QSslKey nullKey; + // Only one row - server must fail to start handshake immediately. + QTest::newRow("valid-server-cert-no-key : VerifyPeer") << QSslSocket::VerifyPeer << chain << nullKey << QString() << false; + } + { + // Valid certificate: + auto chain = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-server.crt")); + QCOMPARE(chain.size(), 1); + + const auto caCert = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-ca.crt")); + QCOMPARE(caCert.size(), 1); + chain += caCert; + + QFile keyFile(certDirPath + QStringLiteral("bogus-server.key")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + auto cert = chain.first(); + const QString name(cert.subjectInfo(QSslCertificate::CommonName).first()); + QTest::newRow("valid-server-cert : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << name << true; + QTest::newRow("valid-server-cert : QueryPeer") << QSslSocket::QueryPeer << chain << key << name << true; + QTest::newRow("valid-server-cert : VerifyNone") << QSslSocket::VerifyNone << chain << key << name << true; + QTest::newRow("valid-server-cert : VerifyPeer (add CA)") << QSslSocket::VerifyPeer << chain << key << name << true; + QTest::newRow("valid-server-cert : VerifyPeer (no CA)") << QSslSocket::VerifyPeer << chain << key << name << false; + QTest::newRow("valid-server-cert : VerifyPeer (name mismatch)") << QSslSocket::VerifyPeer << chain << key << QString() << false; + } +} + +void tst_QDtls::verifyServerCertificate() +{ + QFETCH(const QSslSocket::PeerVerifyMode, verifyMode); + QFETCH(const QList<QSslCertificate>, serverCerts); + QFETCH(const QSslKey, serverKey); + QFETCH(const QString, peerName); + QFETCH(const bool, encrypted); + + auto serverConfig = defaultServerConfig; + serverConfig.setLocalCertificateChain(serverCerts); + serverConfig.setPrivateKey(serverKey); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + + if (serverCerts.size() == 2 && encrypted) { + auto caCerts = clientConfig.caCertificates(); + caCerts.append(serverCerts.at(1)); + clientConfig.setCaCertificates(caCerts); + } + + clientConfig.setPeerVerifyMode(verifyMode); + + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, peerName)); + + connectHandshakeReadingSlots(); + + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + QVERIFY(!testLoop.timeout()); + + if (serverKey.isNull() && !serverCerts.isEmpty()) { + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::TlsInitializationError); + QCOMPARE(serverCrypto->handshakeState(), QDtls::HandshakeNotStarted); + return; + } + + if (encrypted) { + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + } else { + QVERIFY(!clientCrypto->isConnectionEncrypted()); + QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); + QVERIFY(clientCrypto->peerVerificationErrors().size()); + QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, "something") < 0); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + } +} + +void tst_QDtls::verifyClientCertificate_data() +{ +#if !QT_CONFIG(opensslv11) + QSKIP("This test is not supposed to work with OpenSSL version below 1.1"); +#endif + + QTest::addColumn<QSslSocket::PeerVerifyMode>("verifyMode"); + QTest::addColumn<QList<QSslCertificate>>("clientCerts"); + QTest::addColumn<QSslKey>("clientKey"); + QTest::addColumn<bool>("encrypted"); + { + // No certficates, no key: + QList<QSslCertificate> chain; + QSslKey key; + QTest::newRow("no-cert : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << true; + QTest::newRow("no-cert : QueryPeer") << QSslSocket::QueryPeer << chain << key << true; + QTest::newRow("no-cert : VerifyNone") << QSslSocket::VerifyNone << chain << key << true; + QTest::newRow("no-cert : VerifyPeer") << QSslSocket::VerifyPeer << chain << key << false; + } + { + const auto chain = QSslCertificate::fromPath(certDirPath + QStringLiteral("fluke.cert")); + QCOMPARE(chain.size(), 1); + + QFile keyFile(certDirPath + QStringLiteral("fluke.key")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + QTest::newRow("self-signed-cert : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << true; + QTest::newRow("self-signed-cert : QueryPeer") << QSslSocket::QueryPeer << chain << key << true; + QTest::newRow("self-signed-cert : VerifyNone") << QSslSocket::VerifyNone << chain << key << true; + QTest::newRow("self-signed-cert : VerifyPeer") << QSslSocket::VerifyPeer << chain << key << false; + } + { + // Valid certificate, but wrong usage (server certificate): + const auto chain = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-server.crt")); + QCOMPARE(chain.size(), 1); + + QFile keyFile(certDirPath + QStringLiteral("bogus-server.key")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + QTest::newRow("valid-server-cert : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << true; + QTest::newRow("valid-server-cert : QueryPeer") << QSslSocket::QueryPeer << chain << key << true; + QTest::newRow("valid-server-cert : VerifyNone") << QSslSocket::VerifyNone << chain << key << true; + QTest::newRow("valid-server-cert : VerifyPeer") << QSslSocket::VerifyPeer << chain << key << false; + } + { + // Valid certificate, correct usage (client certificate): + auto chain = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-client.crt")); + QCOMPARE(chain.size(), 1); + + QFile keyFile(certDirPath + QStringLiteral("bogus-client.key")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + QTest::newRow("valid-client-cert : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << true; + QTest::newRow("valid-client-cert : QueryPeer") << QSslSocket::QueryPeer << chain << key << true; + QTest::newRow("valid-client-cert : VerifyNone") << QSslSocket::VerifyNone << chain << key << true; + QTest::newRow("valid-client-cert : VerifyPeer") << QSslSocket::VerifyPeer << chain << key << true; + + // Valid certificate, correct usage (client certificate), with chain: + chain += QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-ca.crt")); + QCOMPARE(chain.size(), 2); + + QTest::newRow("valid-client-chain : AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << chain << key << true; + QTest::newRow("valid-client-chain : QueryPeer") << QSslSocket::QueryPeer << chain << key << true; + QTest::newRow("valid-client-chain : VerifyNone") << QSslSocket::VerifyNone << chain << key << true; + QTest::newRow("valid-client-chain : VerifyPeer") << QSslSocket::VerifyPeer << chain << key << true; + } +} + +void tst_QDtls::verifyClientCertificate() +{ + connectHandshakeReadingSlots(); + + QFETCH(const QSslSocket::PeerVerifyMode, verifyMode); + QFETCH(const QList<QSslCertificate>, clientCerts); + QFETCH(const QSslKey, clientKey); + QFETCH(const bool, encrypted); + + QSslConfiguration serverConfig = defaultServerConfig; + serverConfig.setLocalCertificate(selfSignedCert); + serverConfig.setPrivateKey(serverKeySS); + serverConfig.setPeerVerifyMode(verifyMode); + + if (verifyMode == QSslSocket::VerifyPeer && clientCerts.size()) { + // Not always needed even if these conditions met, but does not hurt + // either. + const auto certs = QSslCertificate::fromPath(certDirPath + QStringLiteral("bogus-ca.crt")); + QCOMPARE(certs.size(), 1); + serverConfig.setCaCertificates(serverConfig.caCertificates() + certs); + } + + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + serverConfig = serverCrypto->dtlsConfiguration(); + QVERIFY(serverConfig.peerCertificate().isNull()); + QCOMPARE(serverConfig.peerCertificateChain().size(), 0); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setLocalCertificateChain(clientCerts); + clientConfig.setPrivateKey(clientKey); + clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + + serverConfig = serverCrypto->dtlsConfiguration(); + + if (verifyMode == QSslSocket::VerifyNone || clientCerts.isEmpty()) { + QVERIFY(serverConfig.peerCertificate().isNull()); + QCOMPARE(serverConfig.peerCertificateChain().size(), 0); + } else { + QCOMPARE(serverConfig.peerCertificate(), clientCerts.first()); + QCOMPARE(serverConfig.peerCertificateChain(), clientCerts); + } + + if (encrypted) { + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + } else { + QVERIFY(!serverCrypto->isConnectionEncrypted()); + QCOMPARE(serverCrypto->handshakeState(), QDtls::PeerVerificationFailed); + QVERIFY(serverCrypto->dtlsErrorString().size() > 0); + QVERIFY(serverCrypto->peerVerificationErrors().size() > 0); + + QVERIFY(!clientCrypto->isConnectionEncrypted()); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); + } +} + +void tst_QDtls::blacklistedCerificate() +{ + const auto serverChain = QSslCertificate::fromPath(certDirPath + QStringLiteral("fake-login.live.com.pem")); + QCOMPARE(serverChain.size(), 1); + + QFile keyFile(certDirPath + QStringLiteral("fake-login.live.com.key")); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); + const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + auto serverConfig = defaultServerConfig; + serverConfig.setLocalCertificateChain(serverChain); + serverConfig.setPrivateKey(key); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + connectHandshakeReadingSlots(); + const QString name(serverChain.first().subjectInfo(QSslCertificate::CommonName).first()); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, name)); + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + QVERIFY(!testLoop.timeout()); + QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); + QVERIFY(!clientCrypto->isConnectionEncrypted()); + QVERIFY(verificationErrorDetected(QSslError::CertificateBlacklisted)); +} + +void tst_QDtls::readWriteEncrypted_data() +{ + QTest::addColumn<bool>("serverSideShutdown"); + + QTest::addRow("client-shutdown") << false; + QTest::addRow("server-shutdown") << true; +} + +void tst_QDtls::readWriteEncrypted() +{ + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setLocalCertificate(selfSignedCert); + serverConfig.setPrivateKey(serverKeySS); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setCaCertificates({selfSignedCert}); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); + + // 0. Verify we cannot write any encrypted message without handshake done + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, serverExpectedPlainText) <= 0); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + QVERIFY(!clientCrypto->shutdown(&clientSocket)); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + QDTLS_VERIFY_NO_ERROR(serverCrypto); + QVERIFY(serverCrypto->writeDatagramEncrypted(&serverSocket, clientExpectedPlainText) <= 0); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); + QVERIFY(!serverCrypto->shutdown(&serverSocket)); + QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); + + // 1. Initiate a handshake: + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + // 1.1 Verify we cannot read yet. What the datagram is - not really important, + // invalid state/operation - is what we verify: + const QByteArray dummy = clientCrypto->decryptDatagram(&clientSocket, "BS dgram"); + QCOMPARE(dummy.size(), 0); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); + + // 1.2 Finish the handshake: + testLoop.enterLoopMSecs(handshakeTimeoutMS); + QVERIFY(!testLoop.timeout()); + + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + + // 2. Change reading slots: + connectEncryptedReadingSlots(); + + // 3. Test parameter validation: + QVERIFY(clientCrypto->writeDatagramEncrypted(nullptr, serverExpectedPlainText) <= 0); + QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); + // 4. Write the client's message: + qint64 clientBytesWritten = clientCrypto->writeDatagramEncrypted(&clientSocket, serverExpectedPlainText); + QDTLS_VERIFY_NO_ERROR(clientCrypto); + QVERIFY(clientBytesWritten > 0); + + // 5. Exchange client/server messages: + testLoop.enterLoopMSecs(dataExchangeTimeoutMS); + QVERIFY(!testLoop.timeout()); + + QCOMPARE(serverExpectedPlainText, serverReceivedPlainText); + QCOMPARE(clientExpectedPlainText, clientReceivedPlainText); + + QFETCH(const bool, serverSideShutdown); + DtlsPtr &crypto = serverSideShutdown ? serverCrypto : clientCrypto; + QUdpSocket *socket = serverSideShutdown ? &serverSocket : &clientSocket; + // 6. Parameter validation: + QVERIFY(!crypto->shutdown(nullptr)); + QCOMPARE(crypto->dtlsError(), QDtlsError::InvalidInputParameters); + // 7. Send shutdown alert: + QVERIFY(crypto->shutdown(socket)); + QDTLS_VERIFY_NO_ERROR(crypto); + QCOMPARE(crypto->handshakeState(), QDtls::HandshakeNotStarted); + QVERIFY(!crypto->isConnectionEncrypted()); + // 8. Receive this read notification and handle it: + testLoop.enterLoopMSecs(dataExchangeTimeoutMS); + QVERIFY(!testLoop.timeout()); + + DtlsPtr &peerCrypto = serverSideShutdown ? clientCrypto : serverCrypto; + QVERIFY(!peerCrypto->isConnectionEncrypted()); + QCOMPARE(peerCrypto->handshakeState(), QDtls::HandshakeNotStarted); + QCOMPARE(peerCrypto->dtlsError(), QDtlsError::RemoteClosedConnectionError); +} + +void tst_QDtls::datagramFragmentation() +{ + connectHandshakeReadingSlots(); + + auto serverConfig = defaultServerConfig; + serverConfig.setLocalCertificate(selfSignedCert); + serverConfig.setPrivateKey(serverKeySS); + QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); + + auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); + clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); + QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); + + QVERIFY(clientCrypto->doHandshake(&clientSocket)); + + testLoop.enterLoopMSecs(handshakeTimeoutMS); + QVERIFY(!testLoop.timeout()); + + QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); + QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); + + // Done with handshake, reconnect readyRead: + connectEncryptedReadingSlots(); + + // Verify our dgram is not fragmented and some error set (either UnderlyingSocketError + // if OpenSSL somehow had attempted a write or TlsFatalError in case OpenSSL + // noticed how big the chunk is). + QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, QByteArray(1024 * 17, Qt::Uninitialized)) <= 0); + QVERIFY(clientCrypto->dtlsError() != QDtlsError::NoError); + // Error to write does not mean QDtls is broken: + QVERIFY(clientCrypto->isConnectionEncrypted()); + QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, "Hello, I'm a tiny datagram") > 0); + QDTLS_VERIFY_NO_ERROR(clientCrypto); +} + +void tst_QDtls::handshakeReadyRead() +{ + QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender()); + Q_ASSERT(socket); + + if (!socket->pendingDatagramSize()) + return; + + const bool isServer = socket == &serverSocket; + DtlsPtr &crypto = isServer ? serverCrypto : clientCrypto; + DtlsPtr &peerCrypto = isServer ? clientCrypto : serverCrypto; + QHostAddress addr; + quint16 port = 0; + + QByteArray dgram(socket->pendingDatagramSize(), Qt::Uninitialized); + const qint64 size = socket->readDatagram(dgram.data(), dgram.size(), &addr, &port); + if (size != dgram.size()) + return; + + if (isServer) { + if (!clientPort) { + // It's probably an initial 'ClientHello' message. Let's set remote's + // address/port. But first we make sure it is, indeed, 'ClientHello'. + if (int(dgram.constData()[0]) != 22) + return; + + if (addr.isNull() || addr.isBroadcast()) // Could never be us (client), bail out + return; + + if (!crypto->setPeer(addr, port)) + return testLoop.exitLoop(); + + // Check parameter validation: + if (crypto->doHandshake(nullptr, dgram) || crypto->dtlsError() != QDtlsError::InvalidInputParameters) + return testLoop.exitLoop(); + + if (crypto->doHandshake(&serverSocket, {}) || crypto->dtlsError() != QDtlsError::InvalidInputParameters) + return testLoop.exitLoop(); + + // Make sure we cannot decrypt yet: + const QByteArray dummyDgram = crypto->decryptDatagram(&serverSocket, dgram); + if (dummyDgram.size() > 0 || crypto->dtlsError() != QDtlsError::InvalidOperation) + return testLoop.exitLoop(); + + clientAddress = addr; + clientPort = port; + } else if (clientPort != port || clientAddress != addr) { + return; + } + + if (serverDropDgram) { + serverDropDgram = false; + return; + } + } else if (clientDropDgram) { + clientDropDgram = false; + return; + } + + if (!crypto->doHandshake(socket, dgram)) + return testLoop.exitLoop(); + + const auto state = crypto->handshakeState(); + if (state != QDtls::HandshakeInProgress && state != QDtls::HandshakeComplete) + return testLoop.exitLoop(); + + if (state == QDtls::HandshakeComplete && peerCrypto->handshakeState() == QDtls::HandshakeComplete) + testLoop.exitLoop(); +} + +void tst_QDtls::encryptedReadyRead() +{ + QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender()); + Q_ASSERT(socket); + + if (socket->pendingDatagramSize() <= 0) + return; + + QByteArray dtlsMessage(int(socket->pendingDatagramSize()), Qt::Uninitialized); + QHostAddress addr; + quint16 port = 0; + const qint64 bytesRead = socket->readDatagram(dtlsMessage.data(), dtlsMessage.size(), &addr, &port); + if (bytesRead <= 0) + return; + + dtlsMessage.resize(int(bytesRead)); + + if (socket == &serverSocket) { + if (addr != clientAddress || port != clientPort) + return; + + if (serverExpectedPlainText == dtlsMessage) // No way it can happen! + return testLoop.exitLoop(); + + serverReceivedPlainText = serverCrypto->decryptDatagram(nullptr, dtlsMessage); + if (serverReceivedPlainText.size() > 0 || serverCrypto->dtlsError() != QDtlsError::InvalidInputParameters) + return testLoop.exitLoop(); + + serverReceivedPlainText = serverCrypto->decryptDatagram(&serverSocket, dtlsMessage); + + const int messageType = dtlsMessage.data()[0]; + if (serverReceivedPlainText != serverExpectedPlainText + && (messageType == 23 || messageType == 21)) { + // Type 23 is for application data, 21 is shutdown alert. Here we test + // write/read operations and shutdown alerts, not expecting and thus + // ignoring any other types of messages. + return testLoop.exitLoop(); + } + + if (serverCrypto->dtlsError() != QDtlsError::NoError) + return testLoop.exitLoop(); + + // Verify it cannot be done twice: + const QByteArray replayed = serverCrypto->decryptDatagram(&serverSocket, dtlsMessage); + if (replayed.size() > 0) + return testLoop.exitLoop(); + + if (serverCrypto->writeDatagramEncrypted(&serverSocket, clientExpectedPlainText) <= 0) + testLoop.exitLoop(); + } else { + if (port != serverPort) + return; + + if (clientExpectedPlainText == dtlsMessage) // What a disaster! + return testLoop.exitLoop(); + + clientReceivedPlainText = clientCrypto->decryptDatagram(&clientSocket, dtlsMessage); + testLoop.exitLoop(); + } +} + +void tst_QDtls::pskRequested(QSslPreSharedKeyAuthenticator *auth) +{ + Q_ASSERT(auth); + + auth->setPreSharedKey(presharedKey); +} + +void tst_QDtls::handleHandshakeTimeout() +{ + auto crypto = qobject_cast<QDtls *>(sender()); + Q_ASSERT(crypto); + + if (!crypto->handleTimeout(&clientSocket)) + testLoop.exitLoop(); +} + +void tst_QDtls::clientServerData() +{ + QTest::addColumn<QSslSocket::SslMode>("mode"); + + QTest::addRow("client") << QSslSocket::SslClientMode; + QTest::addRow("server") << QSslSocket::SslServerMode; +} + +void tst_QDtls::connectHandshakeReadingSlots() +{ + connect(&serverSocket, &QUdpSocket::readyRead, this, &tst_QDtls::handshakeReadyRead); + connect(&clientSocket, &QUdpSocket::readyRead, this, &tst_QDtls::handshakeReadyRead); +} + +void tst_QDtls::connectEncryptedReadingSlots() +{ + serverSocket.disconnect(); + clientSocket.disconnect(); + connect(&serverSocket, &QUdpSocket::readyRead, this, &tst_QDtls::encryptedReadyRead); + connect(&clientSocket, &QUdpSocket::readyRead, this, &tst_QDtls::encryptedReadyRead); +} + +bool tst_QDtls::verificationErrorDetected(QSslError::SslError code) const +{ + Q_ASSERT(clientCrypto.data()); + + const auto errors = clientCrypto->peerVerificationErrors(); + for (const QSslError &error : errors) { + if (error.error() == code) + return true; + } + + return false; +} + +QHostAddress tst_QDtls::toNonAny(const QHostAddress &addr) +{ + if (addr == QHostAddress::Any || addr == QHostAddress::AnyIPv4) + return QHostAddress::LocalHost; + if (addr == QHostAddress::AnyIPv6) + return QHostAddress::LocalHostIPv6; + return addr; +} + +QT_END_NAMESPACE + +QTEST_MAIN(tst_QDtls) + +#include "tst_qdtls.moc" |