diff --git a/run-ci.sh b/run-ci.sh index e69694f..970f30c 100755 --- a/run-ci.sh +++ b/run-ci.sh @@ -23,6 +23,14 @@ dub test $DUB_FLAGS if [ "$OS" == "macOS-latest" ]; then dub test :tls -c openssl-1.1 $DUB_FLAGS + DUB_FLAGS="$DUB_FLAGS --override-config vibe-stream:tls/openssl-1.1" else dub test :tls $DUB_FLAGS fi + +if [ ${RUN_TEST=1} -eq 1 ]; then + for ex in `cd tests && ls -1 *.d`; do + echo "[INFO] Running test $ex" + (cd tests && dub --temp-build --single $ex $DUB_FLAGS) + done +fi diff --git a/tests/ca.crt b/tests/ca.crt new file mode 100644 index 0000000..71ecb89 --- /dev/null +++ b/tests/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJAPPsz4Vg1ss1MA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCVNhbXBsZSBDQTAgFw0xNTExMTEwNjM1 +NDFaGA8yMTE1MTAxODA2MzU0MVowWTELMAkGA1UEBhMCREUxEzARBgNVBAgMClNv +bWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAG +A1UEAwwJU2FtcGxlIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +qb+I9X26YydJNXmc7q9woXSI5b/5vitB6BHxUIkXkv0qSKpgV5IGQKXKgJK3FLwf +vqIemiCbJDiiLamnoXectG/FQxxGXolwmMjGBloHSeCo8yZcQq/3WXL2urH2R8B3 +q7Du2TT7pqNFVAvkFVM8Z/rE1kAGBGiF+tTf2Tm0xq3fXk3DOSfTWV9OqHoLNrS7 +Cx2E260L2j1SGk4mCYsDjSWtMm5lfVWZ+FjnAMZw22goMgbPEuMQGhzla0yxux4/ +kKdgyGKFhXAmPsHkDIOtM+HBV34qnPRGps/ICfqAUx+RyUIK5bWjIaUENhFq0uis +dQsX9VDQB489kQJcB/Pt4wIDAQABo1AwTjAdBgNVHQ4EFgQUzQ3L47XiUI3247OT +T1WLDFYr/CgwHwYDVR0jBBgwFoAUzQ3L47XiUI3247OTT1WLDFYr/CgwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAX5cSudTaTbxjMb+SPnQlCZngQWKJ +AjYvQcSR9iXrdmU9teaf/hwZbutTFPgjAWUktNjfxvmgDEbX8PBJ+VfE1DzxZsG/ +V3b1kz6gyFucWcImYu0XEqv4WLA78FuBKvb7XCnrw/Wy8zW0q5Zg6DeHLUd9WABC +omciZhk59qy01r/j4o2KPZxmZ3AM5ZqslC5ntO+mt1TtnYRVLPHAF+5MkApraBLU +e7MaRVNp49FXzsnA50VuIdu5nppuTvb6OeLjVFFujj6mteGIbUSMdI41KkALl4L6 +uqMwL3v4+/BZgTbXJSZcDop/jzMzgGqN5GnpLYx1SFKDxrkAcEEF1bpceA== +-----END CERTIFICATE----- diff --git a/tests/client.crt b/tests/client.crt new file mode 100644 index 0000000..a7f0baf --- /dev/null +++ b/tests/client.crt @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhECAQEwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCREUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDESMBAGA1UEAwwJU2FtcGxlIENBMCAXDTE1MTExMTA2MzkwOVoYDzIxMTUxMDE4 +MDYzOTA5WjBaMQswCQYDVQQGEwJERTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G +A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDDApDbGllbnQg +S2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA23vW1M6yFYc2IjN+ +lqQWS4AzQST7jMf1qfxz4M8O9Ns002MV9jNGxGdqVqiU4QrR1TTB8Rv2Gad9Okqb +B+1Mye766m+Dr2zLEsChKPSNFr/FHJtkpOc7bd7XxlMRJ22Hgoht/lYzjyDk7z4y +1gezrlTjeADo4dEZaR+EqspaKyiIpKM9o+KXmrjrf0rD513tp+soVBUJZ6D4+mlS +kaWBymRNnFCDFxKMDCHrO+Qzdoji/Xv6ebrQg1Y8tAVrVke0cX6R3YcDPLahZnm7 +l/hePK5ZTKIqpYo3sdG9uNlWyBWAsPtG9S6gGPuPJ3IRlMRFvi+cZO1t8KEf/CBz +8pPvNwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB42gKkVADVhB4KLWdhYFJx7RYL +EA1/rTXqguNfiBNBcnj/lWyY8NHaWaSbLSH+rJaCijyB+vk/p8HD6CHNsw/z5wGq +/igDaDdncrQZPgfhstnWCfoCFEB2tVZITJJeG4iya2IT9hZWKxc+cAvFwsVXQidt +cbxI84W26quXQ87FukcS1nJXNofI/RlXyYExm7aoOcwaMxjocv5jY5uvRFbe86dt +VNiIULLfJUTQ3mBuo/idIBV76P6b1Q4SH+YfpYvsxjoaYcUEUKcQ1aXgT5yDxu9e +MAF/1yVEQqgXFWce9N2r0wd3jeoRre1Vs/2Y8R92LP2aKVlKfUlxae30ADtz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJAPPsz4Vg1ss1MA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCVNhbXBsZSBDQTAgFw0xNTExMTEwNjM1 +NDFaGA8yMTE1MTAxODA2MzU0MVowWTELMAkGA1UEBhMCREUxEzARBgNVBAgMClNv +bWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAG +A1UEAwwJU2FtcGxlIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +qb+I9X26YydJNXmc7q9woXSI5b/5vitB6BHxUIkXkv0qSKpgV5IGQKXKgJK3FLwf +vqIemiCbJDiiLamnoXectG/FQxxGXolwmMjGBloHSeCo8yZcQq/3WXL2urH2R8B3 +q7Du2TT7pqNFVAvkFVM8Z/rE1kAGBGiF+tTf2Tm0xq3fXk3DOSfTWV9OqHoLNrS7 +Cx2E260L2j1SGk4mCYsDjSWtMm5lfVWZ+FjnAMZw22goMgbPEuMQGhzla0yxux4/ +kKdgyGKFhXAmPsHkDIOtM+HBV34qnPRGps/ICfqAUx+RyUIK5bWjIaUENhFq0uis +dQsX9VDQB489kQJcB/Pt4wIDAQABo1AwTjAdBgNVHQ4EFgQUzQ3L47XiUI3247OT +T1WLDFYr/CgwHwYDVR0jBBgwFoAUzQ3L47XiUI3247OTT1WLDFYr/CgwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAX5cSudTaTbxjMb+SPnQlCZngQWKJ +AjYvQcSR9iXrdmU9teaf/hwZbutTFPgjAWUktNjfxvmgDEbX8PBJ+VfE1DzxZsG/ +V3b1kz6gyFucWcImYu0XEqv4WLA78FuBKvb7XCnrw/Wy8zW0q5Zg6DeHLUd9WABC +omciZhk59qy01r/j4o2KPZxmZ3AM5ZqslC5ntO+mt1TtnYRVLPHAF+5MkApraBLU +e7MaRVNp49FXzsnA50VuIdu5nppuTvb6OeLjVFFujj6mteGIbUSMdI41KkALl4L6 +uqMwL3v4+/BZgTbXJSZcDop/jzMzgGqN5GnpLYx1SFKDxrkAcEEF1bpceA== +-----END CERTIFICATE----- diff --git a/tests/client.key b/tests/client.key new file mode 100644 index 0000000..2c70607 --- /dev/null +++ b/tests/client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA23vW1M6yFYc2IjN+lqQWS4AzQST7jMf1qfxz4M8O9Ns002MV +9jNGxGdqVqiU4QrR1TTB8Rv2Gad9OkqbB+1Mye766m+Dr2zLEsChKPSNFr/FHJtk +pOc7bd7XxlMRJ22Hgoht/lYzjyDk7z4y1gezrlTjeADo4dEZaR+EqspaKyiIpKM9 +o+KXmrjrf0rD513tp+soVBUJZ6D4+mlSkaWBymRNnFCDFxKMDCHrO+Qzdoji/Xv6 +ebrQg1Y8tAVrVke0cX6R3YcDPLahZnm7l/hePK5ZTKIqpYo3sdG9uNlWyBWAsPtG +9S6gGPuPJ3IRlMRFvi+cZO1t8KEf/CBz8pPvNwIDAQABAoIBAGhnvWbXuADjo82b +H0F7ZpudrUP02rA6QlMNIvYyqNOzHqrbfIv0ElrFMuFsKf3vDqfUA9m0ylmjwb8j +1CFuMVPxmWxBhavCHcua1E7OHftPV+1mwfZrhGqcUZom04ck5Awk5+XPjZEtPZnM +xZPocAGiod3o2N7qf6Lw+kPgJApYyQfmKejppY/AP3H7vQGjSROPqmm9HBWtW3yv +gZ4IJzJA7ZtetYE69IiiyR4HGBAp1TkmCRewFf+G8jbiVfg/IxCzG14EjM7fvjFk +2d3vd542gdXEqn+UOM6tYxSRh36UkB6kod6u3TmhAhRtkzqyM1u8zaFcx9fujrpy +qknu6pkCgYEA9zyAPZfORpfYyF8zIVyY8mOXpVT22XMxbZJeYJSCB0PDFbQIkWwv +UNDGFKKLd9V0Hqz/JEurIEa+O9UfViTa/VJXwmTEPadOu8xk2+000JGT4KFJL8zU +hBdNAG5ROgkfHkHjILVVrHyXri4lS7QibjqUFKbRFJtHN7Afl35imssCgYEA40OA +sGrpbDzs+HJ8sk4yj5aJJppyQIUEAK+IsEyIASUyYtgLPGLogVatLWHCg/GKUHSg +EBlwsic4GdojwQB5SwhFuhqU2Fw9V9/4lQyL0ORc1Gy4kfbuYCgzpIAXdqvk9jOz +elWvQNifAWgP72sT+/EVr0GJJwOw0JCkTQopU8UCgYEA13As/38NDI69JL1wPJ4H +2Q/X0QYcpJewdp6ZBufh9pqhoIPlA3OWN4H1WX3asm2aZjCxk9ssH1AF7PVl/RbY +jO338zkDmAX2occWDF7DKwvwJEnnEHQYq5HyU3HN7hiMuT0IwjPBmDBprCoh7bnm +Vs9MwkIq5x34DRq4LLbBDckCgYEApCh/TW98epWrqm6wSe+YMAXRvdO0EYzDbmdc ++dPeLc2bjrB1tUHQppRgdTZ3Ve1kNmd3swldUSJpMKtURjY8ZwlrrFlFCN/f45jB +q5ArclFyCHMcAzt1xnljjECGMk9KDNkdnJFGDTYr7M0pLYyQG91yB2z0T/0nfA/u +zkM3xO0CgYEAnP6OACxfPTlxeNrFd0wzzh4zO3K8/bZzsrtVFSnKIABQ5024o05e +FTGb/kmXtLPsdarFW5HRFTui44Sf2TDjkowlxj/jcVWSkJ+K0N6t6aH3+Y+8Foir +elRyFwfbndO9MAROSymzfDjwh/zeuFmzudqQfdqiU5DvhNGmaec3dDM= +-----END RSA PRIVATE KEY----- diff --git a/tests/server.crt b/tests/server.crt new file mode 100644 index 0000000..7fda5fd --- /dev/null +++ b/tests/server.crt @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhACAQIwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCREUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDESMBAGA1UEAwwJU2FtcGxlIENBMCAXDTE1MTExMTA2MzkzM1oYDzIxMTUxMDE4 +MDYzOTMzWjBZMQswCQYDVQQGEwJERTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G +A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjqgBkBVyMPsWa64jW +vdhfxeLn69SWZkSv2+HZsNOY/NBORt32sO7Pw6mQLHS+60C7FuMIJRorBkezu5xJ +PPMven5dnjQuPuUqUIeVfH3i5RtdFLiGNk/88+X3068fhmzNxHE+N/IKjjkKiDJD +hd+BBiaZtwdFnEsX6hQ+hT8m0iCcGpiLAwkvdsaWU2cDMvlNnYEiT67cnvdI5NQs +VfOWJxyU10v4DLEl3Fl+rwS6eVoejANP2SlcCcGn9c2yoFABj09L5zefMVZ3uAW4 +KmDi02MTI3ZCjaQK8ILfROGO2R4zStjFeWAjCV9cdMXt4aX1Pgq9UROo14oNnO8S +JaG3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBADU1GFWOikqUHr+a+YrFoodho1kr +5W5tTh3Dmoorbvg38x/cPqUlox+BNXMCaa1liUORlK/5+B6N4dSQw4jgEdrq+cCX +E9449REqejQsLX8LYuQTrXQhDwZ8wqC1iSSauP/AYYWny4ZvLwLpHnpUOZQ2VgO9 ++8kLFSdtFWFi4Saw+TnnbdVwjiYMSsnCSDgtC0p163VsZXuZ32jId1nDSEe/b/mL +nD7VV7GxJgqjqWEQgD4ocvc+CDQ5b76GiWGn4MZmWahmuwbnWaOHL6IQM5jYrnQk +0gV+vusIDBDO0+umtS5tfNdAXLO3tcrJ5+41FtZirZvzxV2+a9FTrlKx2GA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJAPPsz4Vg1ss1MA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCVNhbXBsZSBDQTAgFw0xNTExMTEwNjM1 +NDFaGA8yMTE1MTAxODA2MzU0MVowWTELMAkGA1UEBhMCREUxEzARBgNVBAgMClNv +bWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAG +A1UEAwwJU2FtcGxlIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +qb+I9X26YydJNXmc7q9woXSI5b/5vitB6BHxUIkXkv0qSKpgV5IGQKXKgJK3FLwf +vqIemiCbJDiiLamnoXectG/FQxxGXolwmMjGBloHSeCo8yZcQq/3WXL2urH2R8B3 +q7Du2TT7pqNFVAvkFVM8Z/rE1kAGBGiF+tTf2Tm0xq3fXk3DOSfTWV9OqHoLNrS7 +Cx2E260L2j1SGk4mCYsDjSWtMm5lfVWZ+FjnAMZw22goMgbPEuMQGhzla0yxux4/ +kKdgyGKFhXAmPsHkDIOtM+HBV34qnPRGps/ICfqAUx+RyUIK5bWjIaUENhFq0uis +dQsX9VDQB489kQJcB/Pt4wIDAQABo1AwTjAdBgNVHQ4EFgQUzQ3L47XiUI3247OT +T1WLDFYr/CgwHwYDVR0jBBgwFoAUzQ3L47XiUI3247OTT1WLDFYr/CgwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAX5cSudTaTbxjMb+SPnQlCZngQWKJ +AjYvQcSR9iXrdmU9teaf/hwZbutTFPgjAWUktNjfxvmgDEbX8PBJ+VfE1DzxZsG/ +V3b1kz6gyFucWcImYu0XEqv4WLA78FuBKvb7XCnrw/Wy8zW0q5Zg6DeHLUd9WABC +omciZhk59qy01r/j4o2KPZxmZ3AM5ZqslC5ntO+mt1TtnYRVLPHAF+5MkApraBLU +e7MaRVNp49FXzsnA50VuIdu5nppuTvb6OeLjVFFujj6mteGIbUSMdI41KkALl4L6 +uqMwL3v4+/BZgTbXJSZcDop/jzMzgGqN5GnpLYx1SFKDxrkAcEEF1bpceA== +-----END CERTIFICATE----- diff --git a/tests/server.key b/tests/server.key new file mode 100644 index 0000000..f3617b2 --- /dev/null +++ b/tests/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAo6oAZAVcjD7FmuuI1r3YX8Xi5+vUlmZEr9vh2bDTmPzQTkbd +9rDuz8OpkCx0vutAuxbjCCUaKwZHs7ucSTzzL3p+XZ40Lj7lKlCHlXx94uUbXRS4 +hjZP/PPl99OvH4ZszcRxPjfyCo45CogyQ4XfgQYmmbcHRZxLF+oUPoU/JtIgnBqY +iwMJL3bGllNnAzL5TZ2BIk+u3J73SOTULFXzlicclNdL+AyxJdxZfq8EunlaHowD +T9kpXAnBp/XNsqBQAY9PS+c3nzFWd7gFuCpg4tNjEyN2Qo2kCvCC30ThjtkeM0rY +xXlgIwlfXHTF7eGl9T4KvVETqNeKDZzvEiWhtwIDAQABAoIBAQCfnvhOlOQUbExx +sfJ97h6QGgIZy+pE3W10TvHRTSKyfS/0iC2HojzoKZ7A3Hroka1KSyIWGYmSGMfp +xbrBu047Ki8Aw4GE8Ra01GHIK+jjWCv99m2ZiHMu8/mAYdbU2lhXv5Xq5smL/fYo +DXloXbMEtakEFnxjKfIbWtNvvqk/A/YMuKFzKGMNth5FuAV1XvDb8w7+wwfmVwBe +KsNzEQAZSZPErEi3R7tM7nC55nRyOOGAShdMGFcKtNJtUWLR/QFfPcJc8D04h2yb +JcRiGsL6NBkrSZ5G83l/xkuhZ8MDFu/Jaf3pIeneQyEWIhzKeymrDNOEzZyd7N0X +gRd8Y3xBAoGBANNJrPnnVBxQX5u+BvTKkgpzzzFfPY/i1pz+osm/8N9fFc/GcEX+ +tdxmWJkLLnFpDHuZIc02BZ2sWVFVra78G/WsJUfrfK6YdndXXnM/0actdF2XbsVI +kq93OjEA98wMJuo67KZJiJd3MVuVwrFkgqJhUIEBBzjR23b1p+9WD4/RAoGBAMZM +WovrlYYdirUcWDl8qIB1MoQkTqjwYgrzpX9n7msTxpFr0eYSIx1NauGasyKF2N1c +7LVLjClhUT2A9VE8dx9aZFvwLIb04WmU25CIOfIWv2U5bws3w7LjSXZz0rey7RU7 +wE01uzcZnMoa70yIss8JpQ0Gc3RXoa7Fhc32BUMHAoGAWjqFTvc12R2NpsHA0LrN +IP+RxKDKdm7FfafUNtnr8Ef31ZIFki6S1TszrK+0tqPIu+OKJstiHJJkVjKDiE2L +Vx+bkBoFVb4wIQjDge4IBx+HKQypNkCmu53r0M98ArxRxgdB1kViumQhRN4wlXa5 +vsRWD7Ezt1UPJr8DZ1bWMHECgYEAlWGLjIBKQaAbj9fsdlH8NdkT1wvq3cVIdmuZ +LuNCbhOVsJMCqkjbfwWFELMTY1xlREzuXJyu17ViF+z/4NSmAE597+x7/qaW8Iyc +4sJctIyCXmTnzBGUMTybaMpT4OK/SxGaChACGr4GqRGlV4ha+zCOz8MeUnR5bkmK +RN+ikx0CgYAKuRDHuqgA3pReP9AFRuuysIQdAuDvfP9lXi8U8DRwPop3AeD7Y/1A +q4uGoHHmm4uIPTekFCAiIaojjN2x21SUmQucKiqKg6SHC6Gkt4A17ZWxc/655W6z +X73t+wPK3DLZaFoVWge6BXFcy6NDQODoZnEMH0wfPSAj+lk6Rc5BvA== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls.d b/tests/tls.d new file mode 100644 index 0000000..f7ec824 --- /dev/null +++ b/tests/tls.d @@ -0,0 +1,298 @@ +/+ dub.sdl: + name "tests" + description "TLS tunnel and certificate test" + dependency "vibe-stream:tls" path=".." ++/ +module app; + +import vibe.core.core; +import vibe.core.log; +import vibe.core.stream; +import vibe.stream.operations; +import vibe.stream.wrapper; +import vibe.stream.tls; +import vibe.stream.taskpipe; +import std.encoding : sanitize; + +TLSContext createContext(TLSContextKind kind, string cert, string key, string trust, TLSPeerValidationMode mode) +{ + auto ctx = createTLSContext(kind); + ctx.peerValidationMode = mode; + if (cert.length) ctx.useCertificateChainFile(cert); + if (key.length) ctx.usePrivateKeyFile(key); + if (trust.length) ctx.useTrustedCertificateFile(trust); + return ctx; +} + +void createPipePair(out Stream a, out Stream b) +{ + auto p1 = new TaskPipe; + auto p2 = new TaskPipe; + a = createProxyStream(p1, p2); + b = createProxyStream(p2, p1); +} + +enum Expected { + success, + fail, + dontCare +} + +void testConn( + Expected cli_expect, string cli_cert, string cli_key, string cli_trust, string cli_peer, TLSPeerValidationMode cli_mode, + Expected srv_expect, string srv_cert, string srv_key, string srv_trust, string srv_peer, TLSPeerValidationMode srv_mode) +{ + Stream ctunnel, stunnel; + logInfo("Test client %s (%s, %s, %s, %s), server %s (%s, %s, %s, %s)", + cli_expect, cli_cert, cli_key, cli_trust, cli_peer, + srv_expect, srv_cert, srv_key, srv_trust, srv_peer); + + createPipePair(ctunnel, stunnel); + auto t1 = runTask({ + try { + auto sctx = createContext(TLSContextKind.server, srv_cert, srv_key, srv_trust, srv_mode); + TLSStream sconn; + try { + sconn = createTLSStream(stunnel, sctx, TLSStreamState.accepting, srv_peer); + logDiagnostic("Successfully initiated server tunnel."); + assert(srv_expect != Expected.fail, "Server expected to fail TLS connection."); + } catch (Exception e) { + if (srv_expect == Expected.dontCare) logDiagnostic("Server tunnel failed (dont-care): %s", e.msg); + else if (srv_expect == Expected.fail) logDiagnostic("Server tunnel failed as expected: %s", e.msg); + else { + logError("Server tunnel failed: %s", e.toString().sanitize); + assert(false, "Server not expected to fail TLS connection."); + } + return; + } + if (cli_expect == Expected.fail) return; + assert(sconn.readLine() == "foo"); + sconn.write("bar\r\n"); + sconn.finalize(); + } catch (Exception e) assert(false, e.msg); + }); + auto t2 = runTask({ + try { + auto cctx = createContext(TLSContextKind.client, cli_cert, cli_key, cli_trust, cli_mode); + TLSStream cconn; + try { + cconn = createTLSStream(ctunnel, cctx, TLSStreamState.connecting, cli_peer); + logDiagnostic("Successfully initiated client tunnel."); + assert(cli_expect != Expected.fail, "Client expected to fail TLS connection."); + } catch (Exception e) { + if (cli_expect == Expected.dontCare) logDiagnostic("Client tunnel failed (dont-care): %s", e.msg); + else if (cli_expect == Expected.fail) logDiagnostic("Client tunnel failed as expected: %s", e.msg); + else { + logError("Client tunnel failed: %s", e.toString().sanitize); + assert(false, "Client not expected to fail TLS connection."); + } + return; + } + if (srv_expect == Expected.fail) return; + cconn.write("foo\r\n"); + assert(cconn.readLine() == "bar"); + cconn.finalize(); + } catch (Exception e) assert(false, e.msg); + }); + + t1.join(); + t2.join(); +} + +void testValidation() +{ + // + // Server certificates + // + + // fail for untrusted server cert + testConn( + Expected.fail, null, null, null, "localhost", TLSPeerValidationMode.trustedCert, + Expected.fail, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed for untrusted server cert with disabled validation + testConn( + Expected.success, null, null, null, null, TLSPeerValidationMode.none, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed for untrusted server cert if ignored + testConn( + Expected.success, null, null, null, "localhost", TLSPeerValidationMode.requireCert|TLSPeerValidationMode.checkPeer, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // fail for trusted server cert with no/wrong host name + testConn( + Expected.fail, null, null, "ca.crt", "wronghost", TLSPeerValidationMode.trustedCert, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed for trusted server cert with no/wrong host name if ignored + testConn( + Expected.success, null, null, "ca.crt", "wronghost", TLSPeerValidationMode.trustedCert & ~TLSPeerValidationMode.checkPeer, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed for trusted server cert + testConn( + Expected.success, null, null, "ca.crt", "localhost", TLSPeerValidationMode.trustedCert, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed with no certificates + /*testConn( + false, null, null, null, null, + false, null, null, null, null + );*/ + + // + // Client certificates + // + + // fail for untrusted server cert + testConn( + Expected.dontCare, "client.crt", "client.key", null, null, TLSPeerValidationMode.none, + Expected.fail, "server.crt", "server.key", null, null, TLSPeerValidationMode.trustedCert + ); + + // succeed for untrusted server cert with disabled validation + testConn( + Expected.success, "client.crt", "client.key", null, null, TLSPeerValidationMode.none, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.none + ); + + // succeed for untrusted server cert if ignored + testConn( + Expected.success, "client.crt", "client.key", null, null, TLSPeerValidationMode.none, + Expected.success, "server.crt", "server.key", null, null, TLSPeerValidationMode.requireCert + ); + + // succeed for trusted server cert + testConn( + Expected.success, "client.crt", "client.key", null, null, TLSPeerValidationMode.none, + Expected.success, "server.crt", "server.key", "ca.crt", null, TLSPeerValidationMode.trustedCert & ~TLSPeerValidationMode.checkPeer + ); +} + + +void testConn(TLSVersion cli_version, TLSVersion srv_version, bool expect_success) +{ + Stream ctunnel, stunnel; + logInfo("Test for %s client %s, server %s", expect_success ? "success" : "failure", + cli_version, srv_version); + + createPipePair(ctunnel, stunnel); + auto t1 = runTask({ + try { + TLSContext sctx; + try sctx = createTLSContext(TLSContextKind.server, srv_version); + catch (Exception e) { + assert(!expect_success, "Failed to create TLS context: " ~ e.msg); + ctunnel.finalize(); + stunnel.finalize(); + return; + } + sctx.useCertificateChainFile("server.crt"); + sctx.usePrivateKeyFile("server.key"); + sctx.peerValidationMode = TLSPeerValidationMode.none; + TLSStream sconn; + try { + sconn = createTLSStream(stunnel, sctx, TLSStreamState.accepting, null); + logDiagnostic("Successfully initiated server tunnel."); + assert(expect_success, "Server expected to fail TLS connection."); + } catch (Exception e) { + if (expect_success) { + logError("Server tunnel failed: %s", e.toString().sanitize); + assert(false, "Server not expected to fail TLS connection."); + } + logDiagnostic("Server tunnel failed as expected: %s", e.msg); + return; + } + if (!expect_success) return; + assert(sconn.readLine() == "foo"); + sconn.write("bar\r\n"); + sconn.finalize(); + } catch (Exception e) assert(false, e.msg); + }); + auto t2 = runTask({ + try { + TLSContext cctx; + try cctx = createTLSContext(TLSContextKind.client, cli_version); + catch (Exception e) { + assert(!expect_success, "Failed to create TLS context: " ~ e.msg); + ctunnel.finalize(); + stunnel.finalize(); + return; + } + cctx.peerValidationMode = TLSPeerValidationMode.none; + TLSStream cconn; + try { + cconn = createTLSStream(ctunnel, cctx, TLSStreamState.connecting, null); + logDiagnostic("Successfully initiated client tunnel."); + assert(expect_success, "Client expected to fail TLS connection."); + } catch (Exception e) { + if (expect_success) { + logError("Client tunnel failed: %s", e.toString().sanitize); + assert(false, "Client not expected to fail TLS connection."); + } + logDiagnostic("Client tunnel failed as expected: %s", e.msg); + ctunnel.finalize(); + stunnel.finalize(); + return; + } + if (!expect_success) return; + cconn.write("foo\r\n"); + assert(cconn.readLine() == "bar"); + cconn.finalize(); + } catch (Exception e) assert(false, e.msg); + }); + + t1.join(); + t2.join(); +} + +void testVersion() +{ + // NOTE: SSLv3 is not supported anymore by current OpenSSL versions + // NOTE: Ubuntu 20.04 has removed support for TLSv1/TLSv1.1 from OpenSSL + version (linux) enum support_old_tls = false; + else enum support_old_tls = true; + + testConn(TLSVersion.ssl3, TLSVersion.any, false); + testConn(TLSVersion.ssl3, TLSVersion.ssl3, false); + testConn(TLSVersion.ssl3, TLSVersion.tls1, false); + testConn(TLSVersion.ssl3, TLSVersion.tls1_1, false); + testConn(TLSVersion.ssl3, TLSVersion.tls1_2, false); + + if (support_old_tls) testConn(TLSVersion.tls1, TLSVersion.any, true); + testConn(TLSVersion.tls1, TLSVersion.ssl3, false); + if (support_old_tls) testConn(TLSVersion.tls1, TLSVersion.tls1, true); + testConn(TLSVersion.tls1, TLSVersion.tls1_1, false); + testConn(TLSVersion.tls1, TLSVersion.tls1_2, false); + + if (support_old_tls) testConn(TLSVersion.tls1_1, TLSVersion.any, true); + testConn(TLSVersion.tls1_1, TLSVersion.ssl3, false); + testConn(TLSVersion.tls1_1, TLSVersion.tls1, false); + if (support_old_tls) testConn(TLSVersion.tls1_1, TLSVersion.tls1_1, true); + testConn(TLSVersion.tls1_1, TLSVersion.tls1_2, false); + + testConn(TLSVersion.tls1_2, TLSVersion.any, true); + testConn(TLSVersion.tls1_2, TLSVersion.ssl3, false); + testConn(TLSVersion.tls1_2, TLSVersion.tls1, false); + testConn(TLSVersion.tls1_2, TLSVersion.tls1_1, false); + testConn(TLSVersion.tls1_2, TLSVersion.tls1_2, true); + + testConn(TLSVersion.any, TLSVersion.any, true); + testConn(TLSVersion.any, TLSVersion.ssl3, false); + if (support_old_tls) testConn(TLSVersion.any, TLSVersion.tls1, true); + if (support_old_tls) testConn(TLSVersion.any, TLSVersion.tls1_1, true); + testConn(TLSVersion.any, TLSVersion.tls1_2, true); +} + +void main() +{ + testValidation(); + testVersion(); +}