blob: e65752c3ea9c3b63d9d00ea2083ba569546f959c [file] [log] [blame]
// Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if KJ_HAS_OPENSSL
#if _WIN32
#include <kj/win32-api-version.h>
#endif
#include "tls.h"
#include <openssl/opensslv.h>
#include <stdlib.h>
#if _WIN32
#include <winsock2.h>
#include <kj/windows-sanity.h>
#else
#include <sys/socket.h>
#endif
#include <kj/async-io.h>
#include <kj/test.h>
namespace kj {
namespace {
// =======================================================================================
// test data
//
// made with make-test-certs.sh
static constexpr char CA_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIGMzCCBBugAwIBAgIUDxGXACZeJ0byrswV8gyWskZF2Q8wDQYJKoZIhvcNAQEL\n"
"BQAwgacxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQH\n"
"DAlQYWxvIEFsdG8xFTATBgNVBAoMDFNhbmRzdG9ybS5pbzEbMBkGA1UECwwSVGVz\n"
"dGluZyBEZXBhcnRtZW50MRcwFQYDVQQDDA5jYS5leGFtcGxlLmNvbTEiMCAGCSqG\n"
"SIb3DQEJARYTZ2FycGx5QHNhbmRzdG9ybS5pbzAgFw0yMDA2MjcwMDQyNTJaGA8y\n"
"MTIwMDYwMzAwNDI1MlowgacxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y\n"
"bmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8xFTATBgNVBAoMDFNhbmRzdG9ybS5pbzEb\n"
"MBkGA1UECwwSVGVzdGluZyBEZXBhcnRtZW50MRcwFQYDVQQDDA5jYS5leGFtcGxl\n"
"LmNvbTEiMCAGCSqGSIb3DQEJARYTZ2FycGx5QHNhbmRzdG9ybS5pbzCCAiIwDQYJ\n"
"KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKpp8VF/WDPw1V1aD36/uDWI4XRk9OaJ\n"
"i8tkAbTPutJ7NU4AWv9OzreKIR1PPhj0DtxVOj5KYRTwL1r4DsFWh6D0rBV7oz7o\n"
"zP8hWznVQBSa2BJ2E4uDD8p5oNz1+O+o4UgSBbOr83Gp5SZGw9KO7cgNql9Id/Ii\n"
"sHYxXdrYdAl6InuR6q52CJgcGqQgpFYG+KYqDiByfX52slyz5FA/dfZxsmoEVFLB\n"
"rgbeuhsJGIoasTkGIdCYJhYI7k2uWtvYNurnhgvlpfPHHuSnJ+aWVdKaQUthgbsy\n"
"T2XHuLYpWx7+B7kCF5B4eKtn3e7yzE7A8jn8Teq6yRNUh1PnM7CRMmz3g4UAxmJT\n"
"F5NyQd14IqveFuOk40Ba8wLoypll5We5tV7StUyvaOlAhi+gGPHfWKk4MGiBoaOV\n"
"52D1+/dkh/abokdKZtE59gJX0MrH6mihfc9KQs7N51IhSs4kG5zGIBdvgXtmP17H\n"
"hixUFi0Y85pqGidW4LLQ1pmK9k0U4gYlwHtqHh8an35/vp/mFhl2BDHcpuYKZ34U\n"
"ZDo9GglfCTVEsUvAnwfhuYN0e0kveSTuRCMltjGg0Fs1h9ljNNuc46W4qIx/d5ls\n"
"aMOTKc3PTtwtqgXOXRFn2U7AUXOgtEqyqpuj5ZcjH2YQ3BL24qAiYEHaBOHM+8qF\n"
"9JLZE64j5dnZAgMBAAGjUzBRMB0GA1UdDgQWBBTmqsbDUpi5hgPbcPESYR9t8jsD\n"
"7jAfBgNVHSMEGDAWgBTmqsbDUpi5hgPbcPESYR9t8jsD7jAPBgNVHRMBAf8EBTAD\n"
"AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQADdVBYClYWqNk1s2gamjGsyQ2r88TWTD6X\n"
"RySVnyQPATWuEctrr6+8qTrbqBP4bTPKE+uTzwk+o5SirdJJAkrcwEsSCFw7J/qf\n"
"5U/mXN+EUuqyiMHOS/vLe5X1enj0I6fqJY2mCGFD7Jr/+el1XXjzRZsLZHmqSxww\n"
"T+UjJP+ffvtq3pq4nMQowxXm+Wub0gFHj5wkKMTIDyqnbjzB9bdVd0crtA+EpYIi\n"
"f8y5WB46g1CngRnMzRQvg5FCmxg57i+mVgiUjUe54VenwK9aeeHIuOdLCZ0RmiNH\n"
"KHPUBct+S/AXx8DCoAdm51EahwMBnlUyISpwJ+LVMWA2R9DOxdhEF0tv5iBsD9rn\n"
"oKIWoa0t/Vwnd2n8wyLhuA7N4yzm0rdBjO/rU6n0atIab5+CEDyLeyWQBVwfCUF5\n"
"XYNxOBJgGfSgJa23KUtn15pS/nSTa6sOtS/Mryc4UuNzxn+3ebNOG4UPlH6miSMK\n"
"yA+5SCyKgrn3idifzrq+XafA2WUnxdBLgJMM4OIPAGNjCCW2P1cP/NVllUTjTy2y\n"
"AIKQ/D9V/DzlbIIT6F3CNnqa9xnrBWTKF1YH/zSB7Gh2xlr0WnOWJVQbNUYet982\n"
"JL5ibRhsiqBgltgQPhKhN/rGuh7Cb28679fQqLXKgOWvV2fC4b2y0v9dG78jGCEE\n"
"LBzBUUmunw==\n"
"-----END CERTIFICATE-----\n";
static constexpr char INTERMEDIATE_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIGETCCA/mgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgacxCzAJBgNVBAYTAlVT\n"
"MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8xFTATBgNV\n"
"BAoMDFNhbmRzdG9ybS5pbzEbMBkGA1UECwwSVGVzdGluZyBEZXBhcnRtZW50MRcw\n"
"FQYDVQQDDA5jYS5leGFtcGxlLmNvbTEiMCAGCSqGSIb3DQEJARYTZ2FycGx5QHNh\n"
"bmRzdG9ybS5pbzAgFw0yMDA2MjcwMDQyNTNaGA8yMTIwMDYwMzAwNDI1M1owgZcx\n"
"CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxTYW5k\n"
"c3Rvcm0uaW8xGzAZBgNVBAsMElRlc3RpbmcgRGVwYXJ0bWVudDEbMBkGA1UEAwwS\n"
"aW50LWNhLmV4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNnYXJwbHlAc2FuZHN0\n"
"b3JtLmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvDRmV4e7F/1K\n"
"TaySC2xq00aYZfKJmwcvBvLM60xMrwDf5KlHWoYzog72q6qNb0TpvaeOlymU1IfR\n"
"DkRIMFz6qd4QpK3Ah0vDtqEsoYE6F7gr2QAAaB7HQhczClh6FFkMCygrHDRQlJcF\n"
"VseTKxnZBUAUhnG7OI8bHMZsprg31SNG1GCXq/CO/rkKIP1Pdwevr8DFHL3LFF03\n"
"vdeo6+f/3LjlsCzVCNsCcAYIScRSl1scj2QYcwP7tTmDRAmU9EZFv9MWSqRIMT4L\n"
"/4tP9AL/pWGdx8RRvbXoLJQf+hQW6YnSorRmIH/xYsvqMaan4P0hkomfIHP4nMYa\n"
"LgI8VsNhTeDZ7IvSF2F73baluTOHhUD5eE/WDffNeslaCoMbH/B3H6ks0zYt/mHG\n"
"mDaw3OxgMYep7TIE+SABOSJV+pbtQWNyM7u2+TYHm2DaxD3quf+BoYUZT01uDtN4\n"
"BSsR7XEzF25w/4lDxqBxGAZ0DzItK0kzqMykSWvDIjpSg/UjRj05sc+5zcgE4pX1\n"
"nOLD+FuB9jVqo6zCiIkHsSI0XHnm4D6awB1UyDwSh8mivfUDT53OpOIwOI3EB/4U\n"
"iZstUKgyXNrXsE6wS/3JfdDZ9xkw4dWV+0FWKJ4W6Y8UgKvQJRChpCUtcuxfaLjX\n"
"/ZIcMRYEFNjFppka/7frNT1VNnRvJ9cCAwEAAaNTMFEwHQYDVR0OBBYEFNnHDzWZ\n"
"NC5pP6njUnf1BMXuQKnnMB8GA1UdIwQYMBaAFOaqxsNSmLmGA9tw8RJhH23yOwPu\n"
"MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAG0ShYBIbwGMDUBj\n"
"9G2vxI4Nx7lip9/wO5NCC2biVxtcjNxwTuCcqCxeSGgdimo82vJTm9Wa1AkoawSv\n"
"+spoXJAeOL5dGmZX3IRD0KuDoabH7F6xbNOG3HphvwAJcKQOitPAgTt2eGXCaBqw\n"
"a1ivxPzchHVd3+LyjLMK5G3zzgW0tZhKp7IYE0eGoSGwrw10ox6ibyswe8evul1N\n"
"r7Z3zFSWqjy6mcyp7PvlImDMYzWHYYdncEaeBHd13TnTQ++AtY3Da2lu7WG5VlcV\n"
"vcRaNx8ZLIUpW7u76F9fH25GFnQdF87dgt/ufntts4NifwRRRdIDAgunQ0Nf/TDy\n"
"ow/P7dobloGl9c5xGyXk9EbYYiK/Iuga1yUKJP3UQhfOTNBsDkyUD/jLg+0bI8T+\n"
"Vv4hKNhzUObXjzA5P7+RoEqnPe8R5wjvea6OJc2MDm5GFrzZOdIEnW/iuaQTE3ol\n"
"PYZVDvgPB+l7IC0brwTvdnvXaGLFu8ICQtNoOuSkbEAysyBWCEGwloYLL3r/0ozb\n"
"k3z5mUZVBwhpQS/GEChfUKiLxk9rbDmnhKASa6FVtycSVfoW/Oh5M65ARf9CFIUv\n"
"cvDYLpQl7Ao8QiDqlQBvitNi665uucsZ/zOdFlTKKAellLASpB/q2Y6J4wkogFNa\n"
"dGRV0WpQkcuMjXK7bzSSLMo0nMCm\n"
"-----END CERTIFICATE-----\n";
static constexpr char HOST_KEY[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"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\n"
"-----END RSA PRIVATE KEY-----\n";
static constexpr char VALID_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIF+jCCA+KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\n"
"MRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxTYW5kc3Rvcm0uaW8xGzAZ\n"
"BgNVBAsMElRlc3RpbmcgRGVwYXJ0bWVudDEbMBkGA1UEAwwSaW50LWNhLmV4YW1w\n"
"bGUuY29tMSIwIAYJKoZIhvcNAQkBFhNnYXJwbHlAc2FuZHN0b3JtLmlvMCAXDTIw\n"
"MDYyNzAwNDI1M1oYDzIxMjAwNjI3MDA0MjUzWjCBkDELMAkGA1UEBhMCVVMxEzAR\n"
"BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDFNhbmRzdG9ybS5pbzEbMBkGA1UE\n"
"CwwSVGVzdGluZyBEZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTEiMCAG\n"
"CSqGSIb3DQEJARYTZ2FycGx5QHNhbmRzdG9ybS5pbzCCAiIwDQYJKoZIhvcNAQEB\n"
"BQADggIPADCCAgoCggIBAMzuvxxieOjyKIGST8X0hOnh7qhNSbwvHmkgjiqam3xr\n"
"CBz34fHfkxWrQ3uBheAemGGJsaSVDfOcGQvuhkwO8XjPVLm7LIsHwAhP7HuZSQNE\n"
"km16PUl5BObCPyzOzQOvodz05qC5cHblh4ercB23H3CVLUjkiXpgi1KmTokkPghl\n"
"tkcxBdNUjP3jpqTfYLZlXow+8DsSLW41/wwxPyCr+sUNhtUWMRKYIApzUI2Y+EdL\n"
"q6Dei7TO4YkHm7nl7Nyn1SBHBFfaF3039rSOBtzU5/TCVL2UDSDFFTrY5fFJix35\n"
"N0/R3Hi/0TNLL1iD1jaFCcQOpuLuIPQ9HhA9ltHISnvOrReuUUWB7zzUF2X/rRu8\n"
"Mr0Sj3j1iJRMyAIF1tlsFxL8xb70Z319ZmHlgOyockWlXAqfR9IuWT9C/BNkbL2H\n"
"ftkDJs68ZHjNNSjCUkwLVIasnTWh+Xc5afFF1r6ExSwIeBe+ODo3Tsox6qE2YtaI\n"
"foqrHN6OLVkBd2M6Y4Wqz95p4Bo/mA6f2TehfEHRUFPojRSsemg0YJypvq4tP8kw\n"
"zENxteG0PRTZi0cAfluVCPTsvd9SccXFuy8Z3Gx4AN9uvvmw9QnS18sTr6RyuNAe\n"
"X/c7IJvfNMXYwXC69qNJxgLGfVe/WtATao7wwNw45lwRk2MSUqTBn4qT+Smz9oZd\n"
"AgMBAAGjUzBRMB0GA1UdDgQWBBRdpPVLjMnJFs7lCtMW6x39Wnwt3TAfBgNVHSME\n"
"GDAWgBTZxw81mTQuaT+p41J39QTF7kCp5zAPBgNVHRMBAf8EBTADAQH/MA0GCSqG\n"
"SIb3DQEBCwUAA4ICAQA2kid2fGqjeDRVuclfDRr0LhbFYfJJXxW7SPgcUpJYXeAz\n"
"LXotBm/Cc+K01nNtl0JYfJy4IkaQUYgfVsA5/FqTGnbRmpEd5XidiGE6PfkXZSNj\n"
"02v6Uv2bAs8NnJirS5F0JhWZ45xAOMbl04QPUdkISF1JzioCcCqWOggbIV7kzwrB\n"
"MTcsx8vuh04S9vB318pKli4uIjNdwu7HnoqbrqhSgTUK1aXS6sDQNN/nvR6F5TRL\n"
"MC0cCtIA6n04c09WRfHxl/YPQwayxGD23eQ9UC7Noe/R8B3K/+6XUYIEmXx6HpnP\n"
"yt/79iBPwLnaVjGDwKfI8EPuSo7AkDSO3uxMjf7eCL2sCzWlgsne9yOfYxGn+q9K\n"
"h3KTOR1b7EVU/G4h7JxlSHqf3Ii9qFba/HsUo1yMjVEraMNpxXCijGsN30fqUpLg\n"
"2g9lNKmIdyHuYdlZET082b1dvb7cfYChlqHvrPv5awbsc1Ka2pOFOMwnUi2w3cHc\n"
"TLq3SyipI+sgJg3HHSJ3zVHrgKUeDoQi52i5WedvIBPfHC4Ik4zEzlkCMDd3hoLe\n"
"THAsBGBBEICK+lLSo0Mst2kxhTHHK+PxmhorXXnGOpla2wbwzVZgxur45axCWVqH\n"
"cbdcVhzR2w4XhjaS74WGiHHn5mHb/uYZJiZGpFCNefU2WOBlRCX7hgAzlPa4ZQ==\n"
"-----END CERTIFICATE-----\n";
static constexpr char HOST_KEY2[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"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\n"
"-----END RSA PRIVATE KEY-----\n";
static constexpr char VALID_CERT2[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIF+jCCA+KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\n"
"MRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxTYW5kc3Rvcm0uaW8xGzAZ\n"
"BgNVBAsMElRlc3RpbmcgRGVwYXJ0bWVudDEbMBkGA1UEAwwSaW50LWNhLmV4YW1w\n"
"bGUuY29tMSIwIAYJKoZIhvcNAQkBFhNnYXJwbHlAc2FuZHN0b3JtLmlvMCAXDTIw\n"
"MDYyNzAwNDI1M1oYDzIxMjAwNjI3MDA0MjUzWjCBkDELMAkGA1UEBhMCVVMxEzAR\n"
"BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDFNhbmRzdG9ybS5pbzEbMBkGA1UE\n"
"CwwSVGVzdGluZyBEZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLm5ldDEiMCAG\n"
"CSqGSIb3DQEJARYTZ2FycGx5QHNhbmRzdG9ybS5pbzCCAiIwDQYJKoZIhvcNAQEB\n"
"BQADggIPADCCAgoCggIBALaVC2wVXHj+VEz/AKv6meM7sLNJoH0Ws5RRQ859EqcS\n"
"kYH1ETqwwfBDHH3y/iMVmHc7M2J5u0eKfLnUB7anV9ZviaVjyHBMjhrihIxBhlQW\n"
"37XohrwmWh5poUhhbgbkUJ7JjjDdrF+AEz4CEnlKSs92HBMOc2xlfV73kAQogmWC\n"
"ThGNkas7s38Jju+jX/9KULRWjer4xzhjqeXusRFlDTK7pYsPN/wWJrThNAh/UJi0\n"
"ouXf7DjLKaV1XbsFLIR79oMKHFxYpOBVIQzCo386e9br0S6gJ6bH+rubkoV8KoGp\n"
"qIUHLYfQ9MeSjKE8JX+y51ednSeBadJ6eBWJDYSScajF4ybs1uB6h8rf883pYV2r\n"
"ysj8n1xppER80CwGjswzft6lwbwK7m2GqbsvwhdZ/w4fTr3BifLytsLWoRooNlq8\n"
"IeZNHmRIkH5BbVyoVZsTYHBBK+nKCDwD3vPNhdB61oDOYf4bkudDAuTlso5wMkme\n"
"y/0I9O4w3zXIo9cmo5r+GobC/BSOIWpfJNw0l3tzA5wMJgcSoPm/ylFjYrnHSJ2V\n"
"Gde3Oe+uWkKHiD2RVQaAEK4BRSzxN9cAu0sKzLijIctlCm4F43GeTDJG0tlgUmqg\n"
"RS0srMx2NHBTwkYeiu3e7w70hu5WU9epTRX2oG73DtbrrEAQ9toBrzThds2Xt++x\n"
"AgMBAAGjUzBRMB0GA1UdDgQWBBSCVMitc7axjQ0JObyQ7SoZ15v41jAfBgNVHSME\n"
"GDAWgBTZxw81mTQuaT+p41J39QTF7kCp5zAPBgNVHRMBAf8EBTADAQH/MA0GCSqG\n"
"SIb3DQEBCwUAA4ICAQAGqI+GGbSHkV9C16OLKgujS17zAJDuMeUZVoUvsh0oj7hK\n"
"QwuJ6M6VIWZXk0Ccs/TbtQgyUtt98HY/M5LYjvuB3jb348TvYvBg1un6DC1LNFnw\n"
"x19eUvwxhoI0I9A/heD6251plaXl0rk+wmTn+gqHNswb0LZw7l8XclOQ8s13/Ei3\n"
"fD4P5N3LiXaPfcXzFtEvWJE1ONC/PvLfwWWE2T+/LabJ4I4iumX8oAJZyx9BCE09\n"
"54/0cV1V6xjp31/CS7vkYtDMeREnydwC3PsjjzO18nM0GVw6R2eok/yvD2Rg/pqJ\n"
"CiscKswcy0OR42pCzJyAwHaXV0KZEG9E97ukiqh3ByBUfR0ZwkKv7tDaL7UQiXdF\n"
"sheJ3l8TyQNcuWljjm1MWJt9ZzZt5zE4+yes4YVDNNe9l2jIoT8641pcy2MmPOdJ\n"
"8pEE3xJ2SAdeJKVXuHoi7glzmlK1O5nSNK3GIfKRwJ2hmIXSAoMPfpwtJWdJDNGZ\n"
"N2HThXDMleMrJqwsdToRCp0nBm40cKSDk/o7SfiE7z4e1EVDAFBlWl5SAq9Pqwh5\n"
"lBlsQXd5SbzWGyVk7BjtT3ttbXru9NEINo1l9Cw74GQuW40FsQf4drZVDVtaNWPd\n"
"IvZM211bcU/zZV44rkz3nc08jSGo2qP8bEcuYAlTneDLyrAmpisUXKYm1Q1SxA==\n"
"-----END CERTIFICATE-----\n";
static constexpr char EXPIRED_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIF+DCCA+CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\n"
"MRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxTYW5kc3Rvcm0uaW8xGzAZ\n"
"BgNVBAsMElRlc3RpbmcgRGVwYXJ0bWVudDEbMBkGA1UEAwwSaW50LWNhLmV4YW1w\n"
"bGUuY29tMSIwIAYJKoZIhvcNAQkBFhNnYXJwbHlAc2FuZHN0b3JtLmlvMB4XDTE2\n"
"MDEwMTAwMDAwMFoXDTE2MDEwMTAwMDAwMFowgZAxCzAJBgNVBAYTAlVTMRMwEQYD\n"
"VQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxTYW5kc3Rvcm0uaW8xGzAZBgNVBAsM\n"
"ElRlc3RpbmcgRGVwYXJ0bWVudDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIjAgBgkq\n"
"hkiG9w0BCQEWE2dhcnBseUBzYW5kc3Rvcm0uaW8wggIiMA0GCSqGSIb3DQEBAQUA\n"
"A4ICDwAwggIKAoICAQDM7r8cYnjo8iiBkk/F9ITp4e6oTUm8Lx5pII4qmpt8awgc\n"
"9+Hx35MVq0N7gYXgHphhibGklQ3znBkL7oZMDvF4z1S5uyyLB8AIT+x7mUkDRJJt\n"
"ej1JeQTmwj8szs0Dr6Hc9OaguXB25YeHq3Adtx9wlS1I5Il6YItSpk6JJD4IZbZH\n"
"MQXTVIz946ak32C2ZV6MPvA7Ei1uNf8MMT8gq/rFDYbVFjESmCAKc1CNmPhHS6ug\n"
"3ou0zuGJB5u55ezcp9UgRwRX2hd9N/a0jgbc1Of0wlS9lA0gxRU62OXxSYsd+TdP\n"
"0dx4v9EzSy9Yg9Y2hQnEDqbi7iD0PR4QPZbRyEp7zq0XrlFFge881Bdl/60bvDK9\n"
"Eo949YiUTMgCBdbZbBcS/MW+9Gd9fWZh5YDsqHJFpVwKn0fSLlk/QvwTZGy9h37Z\n"
"AybOvGR4zTUowlJMC1SGrJ01ofl3OWnxRda+hMUsCHgXvjg6N07KMeqhNmLWiH6K\n"
"qxzeji1ZAXdjOmOFqs/eaeAaP5gOn9k3oXxB0VBT6I0UrHpoNGCcqb6uLT/JMMxD\n"
"cbXhtD0U2YtHAH5blQj07L3fUnHFxbsvGdxseADfbr75sPUJ0tfLE6+kcrjQHl/3\n"
"OyCb3zTF2MFwuvajScYCxn1Xv1rQE2qO8MDcOOZcEZNjElKkwZ+Kk/kps/aGXQID\n"
"AQABo1MwUTAdBgNVHQ4EFgQUXaT1S4zJyRbO5QrTFusd/Vp8Ld0wHwYDVR0jBBgw\n"
"FoAU2ccPNZk0Lmk/qeNSd/UExe5AqecwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG\n"
"9w0BAQsFAAOCAgEAYRQazCV707BpbBo3j2PXfg4rmrm1GIA0JXFsY27CII0aSgTw\n"
"roMBnwp3sJ+UIxqddkwf/4Bn/kq8yHu8WMc1cb4bzsqgU4K2zGJcVF3i9Y4R6oE1\n"
"9Y6QM1db5HYiCdXSNW5uZUQGButyIXsUfPns0jMZfmEhsW4WrN3m2qE357FeBfCF\n"
"nP4Ij3sbUq01OoPBL6sWUbltfL5PgqKitE6UFu1/WFpBatP+8ITOOLhkGmJ70zb1\n"
"rY3jnwlEaRJVw8DmIkkabrgKu2gUZ3qKX9aeW9iJW524A2ZjEg1xoBRq9MVcfiRN\n"
"qAERaEd7MgjIFmOYR5O2juzR3eMrv+U+JsY6K5ItTwwE/MHKjYRM22CgmQahPfvo\n"
"o40qLUn/zJNJAN+1hrmOqFXpC5vmfKV9pcG7BOsuZ9V9gkssnJRosCuU8iIW3gyo\n"
"C6TFLIneStvBzokoCTv0Fxxh/vqfIWmHYr7nsFM8S/X2iDLuCoiB6qsRw5NaOIg6\n"
"QB7cEi3sgBZU+eJDmynR3waIU0HGmj9DK8Tc2TMd5wvkBpJBkqqQkICVQ/u/g7up\n"
"swvT5Iap509sI4nmKqe9meN6m3xBSJrCNPTbjUyu7PuC/rBe7lVwnP5/PN/aj6ZU\n"
"XGyLwArQ/5GgT2sy3aEQQTtb+kthnZo7NL8nmkpoTbrm84DJ4dwkD4qc+hs=\n"
"-----END CERTIFICATE-----\n";
static constexpr char SELF_SIGNED_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"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\n"
"SQ==\n"
"-----END CERTIFICATE-----\n";
// =======================================================================================
class ErrorNexus {
// Helper class that wraps various promises such that if one throws an exception, they all do.
public:
ErrorNexus(): ErrorNexus(kj::newPromiseAndFulfiller<void>()) {}
template <typename T>
kj::Promise<T> wrap(kj::Promise<T>&& promise) {
return promise.catch_([this](kj::Exception&& e) -> kj::Promise<T> {
fulfiller->reject(kj::cp(e));
return kj::mv(e);
}).exclusiveJoin(failurePromise.addBranch().then([]() -> T { KJ_UNREACHABLE; }));
}
private:
kj::ForkedPromise<void> failurePromise;
kj::Own<kj::PromiseFulfiller<void>> fulfiller;
ErrorNexus(kj::PromiseFulfillerPair<void> paf)
: failurePromise(kj::mv(paf.promise).fork()),
fulfiller(kj::mv(paf.fulfiller)) {}
};
struct TlsTest {
kj::AsyncIoContext io = setupAsyncIo();
TlsContext tlsClient;
TlsContext tlsServer;
TlsTest(TlsContext::Options clientOpts = defaultClient(),
TlsContext::Options serverOpts = defaultServer())
: tlsClient(kj::mv(clientOpts)),
tlsServer(kj::mv(serverOpts)) {}
static TlsContext::Options defaultServer() {
static TlsKeypair keypair = {
TlsPrivateKey(HOST_KEY),
TlsCertificate(kj::str(VALID_CERT, INTERMEDIATE_CERT))
};
TlsContext::Options options;
options.defaultKeypair = keypair;
return options;
}
static TlsContext::Options defaultClient() {
static TlsCertificate caCert(CA_CERT);
TlsContext::Options options;
options.useSystemTrustStore = false;
options.trustedCertificates = kj::arrayPtr(&caCert, 1);
return options;
}
Promise<void> writeToServer(AsyncIoStream& client) {
return client.write("foo", 4);
}
Promise<void> readFromClient(AsyncIoStream& server) {
auto buf = heapArray<char>(4);
auto readPromise = server.read(buf.begin(), buf.size());
auto checkBuffer = [buf = kj::mv(buf)]() {
KJ_ASSERT(kj::StringPtr(buf.begin(), buf.end()-1) == kj::StringPtr("foo"));
};
return readPromise.then(kj::mv(checkBuffer));
}
void testConnection(AsyncIoStream& client, AsyncIoStream& server) {
auto writePromise = writeToServer(client);
auto readPromise = readFromClient(server);
writePromise.wait(io.waitScope);
readPromise.wait(io.waitScope);
};
};
KJ_TEST("TLS basics") {
TlsTest test;
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
test.testConnection(*client, *server);
}
KJ_TEST("TLS peer identity") {
TlsTest test;
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto innerClientId = kj::LocalPeerIdentity::newInstance({});
auto& innerClientIdRef = *innerClientId;
auto clientPromise = e.wrap(test.tlsClient.wrapClient(
kj::AuthenticatedStream { kj::mv(pipe.ends[0]), kj::mv(innerClientId) },
"example.com"));
auto innerServerId = kj::LocalPeerIdentity::newInstance({});
auto& innerServerIdRef = *innerServerId;
auto serverPromise = e.wrap(test.tlsServer.wrapServer(
kj::AuthenticatedStream { kj::mv(pipe.ends[1]), kj::mv(innerServerId) }));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
{
auto id = client.peerIdentity.downcast<TlsPeerIdentity>();
KJ_ASSERT(id->hasCertificate());
KJ_EXPECT(id->getCommonName() == "example.com");
KJ_EXPECT(&id->getNetworkIdentity() == &innerClientIdRef);
KJ_EXPECT(id->toString() == "example.com");
}
{
auto id = server.peerIdentity.downcast<TlsPeerIdentity>();
KJ_EXPECT(!id->hasCertificate());
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE(
"client did not provide a certificate", id->getCommonName());
KJ_EXPECT(&id->getNetworkIdentity() == &innerServerIdRef);
KJ_EXPECT(id->toString() == "(anonymous client)");
}
test.testConnection(*client.stream, *server.stream);
}
KJ_TEST("TLS multiple messages") {
TlsTest test;
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto writePromise = client->write("foo", 3)
.then([&]() { return client->write("bar", 3); });
char buf[4];
buf[3] = '\0';
server->read(&buf, 3).wait(test.io.waitScope);
KJ_ASSERT(kj::StringPtr(buf) == "foo");
writePromise = writePromise
.then([&]() { return client->write("baz", 3); });
server->read(&buf, 3).wait(test.io.waitScope);
KJ_ASSERT(kj::StringPtr(buf) == "bar");
server->read(&buf, 3).wait(test.io.waitScope);
KJ_ASSERT(kj::StringPtr(buf) == "baz");
auto readPromise = server->read(&buf, 3);
KJ_EXPECT(!readPromise.poll(test.io.waitScope));
writePromise = writePromise
.then([&]() { return client->write("qux", 3); });
readPromise.wait(test.io.waitScope);
KJ_ASSERT(kj::StringPtr(buf) == "qux");
}
KJ_TEST("TLS zero-sized write") {
TlsTest test;
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
char buf[7];
auto readPromise = server->read(&buf, 6);
client->write("", 0).wait(test.io.waitScope);
client->write("foo", 3).wait(test.io.waitScope);
client->write("", 0).wait(test.io.waitScope);
client->write("bar", 3).wait(test.io.waitScope);
readPromise.wait(test.io.waitScope);
buf[6] = '\0';
KJ_ASSERT(kj::StringPtr(buf) == "foobar");
}
kj::Promise<void> writeN(kj::AsyncIoStream& stream, kj::StringPtr text, size_t count) {
if (count == 0) return kj::READY_NOW;
--count;
return stream.write(text.begin(), text.size())
.then([&stream, text, count]() {
return writeN(stream, text, count);
});
}
kj::Promise<void> readN(kj::AsyncIoStream& stream, kj::StringPtr text, size_t count) {
if (count == 0) return kj::READY_NOW;
--count;
auto buf = kj::heapString(text.size());
auto promise = stream.read(buf.begin(), buf.size());
return promise.then(kj::mvCapture(buf, [&stream, text, count](kj::String buf) {
KJ_ASSERT(buf == text, buf, text, count);
return readN(stream, text, count);
}));
}
KJ_TEST("TLS full duplex") {
TlsTest test;
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
#if _WIN32
// On Windows we observe that `writeUp`, below, completes before the other end has started
// reading, failing the `!writeUp.poll()` expectation. I guess Windows has big buffers. We can
// fix this by requesting small buffers here. (Worth keeping in mind that Windows doesn't have
// socketpairs, so `newTwoWayPipe()` is implemented in terms of loopback TCP, ugh.)
uint small = 256;
pipe.ends[0]->setsockopt(SOL_SOCKET, SO_SNDBUF, &small, sizeof(small));
pipe.ends[0]->setsockopt(SOL_SOCKET, SO_RCVBUF, &small, sizeof(small));
#endif
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto writeUp = writeN(*client, "foo", 10000);
auto readDown = readN(*client, "bar", 10000);
KJ_EXPECT(!writeUp.poll(test.io.waitScope));
KJ_EXPECT(!readDown.poll(test.io.waitScope));
auto writeDown = writeN(*server, "bar", 10000);
auto readUp = readN(*server, "foo", 10000);
readUp.wait(test.io.waitScope);
readDown.wait(test.io.waitScope);
writeUp.wait(test.io.waitScope);
writeDown.wait(test.io.waitScope);
}
class TestSniCallback: public TlsSniCallback {
public:
kj::Maybe<TlsKeypair> getKey(kj::StringPtr hostname) override {
++callCount;
KJ_ASSERT(hostname == "example.com");
return TlsKeypair {
TlsPrivateKey(HOST_KEY),
TlsCertificate(kj::str(VALID_CERT, INTERMEDIATE_CERT))
};
}
uint callCount = 0;
};
KJ_TEST("TLS SNI") {
TlsContext::Options serverOptions;
TestSniCallback callback;
serverOptions.sniCallback = callback;
TlsTest test(TlsTest::defaultClient(), kj::mv(serverOptions));
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
test.testConnection(*client, *server);
KJ_ASSERT(callback.callCount == 1);
}
void expectInvalidCert(kj::StringPtr hostname, TlsCertificate cert, kj::StringPtr message) {
TlsKeypair keypair = { TlsPrivateKey(HOST_KEY), kj::mv(cert) };
TlsContext::Options serverOpts;
serverOpts.defaultKeypair = keypair;
TlsTest test(TlsTest::defaultClient(), kj::mv(serverOpts));
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), hostname));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
KJ_EXPECT_THROW_MESSAGE(message, clientPromise.wait(test.io.waitScope));
}
KJ_TEST("TLS certificate validation") {
expectInvalidCert("wrong.com", TlsCertificate(kj::str(VALID_CERT, INTERMEDIATE_CERT)),
"Hostname mismatch");
expectInvalidCert("example.com", TlsCertificate(VALID_CERT),
"unable to get local issuer certificate");
expectInvalidCert("example.com", TlsCertificate(kj::str(EXPIRED_CERT, INTERMEDIATE_CERT)),
"certificate has expired");
expectInvalidCert("example.com", TlsCertificate(SELF_SIGNED_CERT),
"self signed certificate");
}
// BoringSSL seems to print error messages differently.
#ifdef OPENSSL_IS_BORINGSSL
#define SSL_MESSAGE(interesting, boring) boring
#else
#define SSL_MESSAGE(interesting, boring) interesting
#endif
KJ_TEST("TLS client certificate verification") {
enum class VerifyClients {
YES,
NO
};
auto makeServerOptionsForClient = [](
const TlsContext::Options& clientOptions,
VerifyClients verifyClients
) {
TlsContext::Options serverOptions = TlsTest::defaultServer();
serverOptions.verifyClients = verifyClients == VerifyClients::YES;
// Share the certs between the client and server.
serverOptions.trustedCertificates = clientOptions.trustedCertificates;
return serverOptions;
};
TlsKeypair selfSignedKeypair = { TlsPrivateKey(HOST_KEY), TlsCertificate(SELF_SIGNED_CERT) };
TlsKeypair altKeypair = {
TlsPrivateKey(HOST_KEY2),
TlsCertificate(kj::str(VALID_CERT2, INTERMEDIATE_CERT)),
};
{
// No certificate loaded in the client: fail
auto clientOptions = TlsTest::defaultClient();
auto serverOptions = makeServerOptionsForClient(clientOptions, VerifyClients::YES);
TlsTest test(kj::mv(clientOptions), kj::mv(serverOptions));
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com")
.then([](kj::Own<kj::AsyncIoStream> stream) {
auto promise = stream->readAllBytes();
return promise.attach(kj::mv(stream));
});
auto serverPromise = test.tlsServer.wrapServer(kj::mv(pipe.ends[1]));
KJ_EXPECT_THROW_MESSAGE(
SSL_MESSAGE("peer did not return a certificate",
"PEER_DID_NOT_RETURN_A_CERTIFICATE"),
serverPromise.wait(test.io.waitScope));
#if !KJ_NO_EXCEPTIONS // if exceptions are disabled, we're now in a bad state because
// KJ_EXPECT_THROW_MESSAGE() runs in a forked child process.
KJ_EXPECT_THROW_MESSAGE(
SSL_MESSAGE("alert", // "alert handshake failure" or "alert certificate required"
"ALERT"), // "ALERT_HANDSHAKE_FAILURE" or "ALERT_CERTIFICATE_REQUIRED"
clientPromise.wait(test.io.waitScope));
#endif
}
{
// Self-signed certificate loaded in the client: fail
auto clientOptions = TlsTest::defaultClient();
clientOptions.defaultKeypair = selfSignedKeypair;
auto serverOptions = makeServerOptionsForClient(clientOptions, VerifyClients::YES);
TlsTest test(kj::mv(clientOptions), kj::mv(serverOptions));
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com")
.then([](kj::Own<kj::AsyncIoStream> stream) {
auto promise = stream->readAllBytes();
return promise.attach(kj::mv(stream));
});
auto serverPromise = test.tlsServer.wrapServer(kj::mv(pipe.ends[1]));
KJ_EXPECT_THROW_MESSAGE(
SSL_MESSAGE("certificate verify failed",
"CERTIFICATE_VERIFY_FAILED"),
serverPromise.wait(test.io.waitScope));
#if !KJ_NO_EXCEPTIONS // if exceptions are disabled, we're now in a bad state because
// KJ_EXPECT_THROW_MESSAGE() runs in a forked child process.
KJ_EXPECT_THROW_MESSAGE(
SSL_MESSAGE("alert unknown ca",
"TLSV1_ALERT_UNKNOWN_CA"),
clientPromise.wait(test.io.waitScope));
#endif
}
{
// Trusted certificate loaded in the client: success.
auto clientOptions = TlsTest::defaultClient();
clientOptions.defaultKeypair = altKeypair;
auto serverOptions = makeServerOptionsForClient(clientOptions, VerifyClients::YES);
TlsTest test(kj::mv(clientOptions), kj::mv(serverOptions));
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(
kj::AuthenticatedStream { kj::mv(pipe.ends[1]), kj::UnknownPeerIdentity::newInstance() }));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto id = server.peerIdentity.downcast<TlsPeerIdentity>();
KJ_ASSERT(id->hasCertificate());
KJ_EXPECT(id->getCommonName() == "example.net");
test.testConnection(*client, *server.stream);
}
{
// If verifyClients is off, client certificate is ignored, even if trusted.
auto clientOptions = TlsTest::defaultClient();
auto serverOptions = makeServerOptionsForClient(clientOptions, VerifyClients::NO);
TlsTest test(kj::mv(clientOptions), kj::mv(serverOptions));
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(
kj::AuthenticatedStream { kj::mv(pipe.ends[1]), kj::UnknownPeerIdentity::newInstance() }));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto id = server.peerIdentity.downcast<TlsPeerIdentity>();
KJ_EXPECT(!id->hasCertificate());
}
{
// Non-trusted keys are ignored too (not errors).
auto clientOptions = TlsTest::defaultClient();
clientOptions.defaultKeypair = selfSignedKeypair;
auto serverOptions = makeServerOptionsForClient(clientOptions, VerifyClients::NO);
TlsTest test(kj::mv(clientOptions), kj::mv(serverOptions));
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(
kj::AuthenticatedStream { kj::mv(pipe.ends[1]), kj::UnknownPeerIdentity::newInstance() }));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto id = server.peerIdentity.downcast<TlsPeerIdentity>();
KJ_EXPECT(!id->hasCertificate());
}
}
class MockConnectionReceiver final: public ConnectionReceiver {
// This connection receiver allows mocked async connection establishment without the network.
struct ClientRequest {
Maybe<Exception> maybeException;
Own<PromiseFulfiller<Own<AsyncIoStream>>> clientFulfiller;
};
public:
MockConnectionReceiver(AsyncIoProvider& provider): provider(provider) {}
Promise<Own<AsyncIoStream>> accept() override {
return acceptImpl();
}
Promise<AuthenticatedStream> acceptAuthenticated() override {
return acceptImpl().then([](auto stream) -> AuthenticatedStream {
return { kj::mv(stream), LocalPeerIdentity::newInstance({}) };
});
}
uint getPort() override {
return 0;
}
void getsockopt(int, int, void*, uint*) override {}
void setsockopt(int, int, const void*, uint) override {}
Promise<Own<AsyncIoStream>> connect() {
// Mock a new successful connection to our receiver.
return connectImpl();
}
Promise<void> badConnect() {
// Mock a new failed connection to our receiver.
return connectImpl(KJ_EXCEPTION(DISCONNECTED, "Pipes are leaky")).ignoreResult();
}
private:
Promise<Own<AsyncIoStream>> acceptImpl() {
if (clientRequests.empty()) {
KJ_ASSERT(!serverFulfiller);
auto paf = newPromiseAndFulfiller<void>();
serverFulfiller = kj::mv(paf.fulfiller);
return paf.promise.then([this] {
return acceptImpl();
});
}
// This is accepting in FILO order, it shouldn't matter in practice.
auto request = kj::mv(clientRequests.back());
clientRequests.removeLast();
KJ_IF_MAYBE(exception, kj::mv(request.maybeException)) {
request.clientFulfiller = nullptr; // The other end had an issue, break the promise.
return kj::mv(*exception);
} else {
auto pipe = provider.newTwoWayPipe();
request.clientFulfiller->fulfill(kj::mv(pipe.ends[0]));
return kj::mv(pipe.ends[1]);
}
}
Promise<Own<AsyncIoStream>> connectImpl(Maybe<Exception> maybeException = nullptr) {
auto paf = newPromiseAndFulfiller<Own<AsyncIoStream>>();
clientRequests.add(ClientRequest{ kj::mv(maybeException), kj::mv(paf.fulfiller) });
if (auto fulfiller = kj::mv(serverFulfiller)) {
fulfiller->fulfill();
}
return kj::mv(paf.promise);
}
AsyncIoProvider& provider;
Own<PromiseFulfiller<void>> serverFulfiller;
Vector<ClientRequest> clientRequests;
};
class TlsReceiverTest final: public TlsTest {
// TlsReceiverTest augments TlsTest to test TlsConnectionReceiver.
public:
TlsReceiverTest(): TlsTest() {
auto baseReceiverPtr = kj::heap<MockConnectionReceiver>(*io.provider);
baseReceiver = baseReceiverPtr.get();
receiver = tlsServer.wrapPort(kj::mv(baseReceiverPtr));
}
TlsReceiverTest(TlsReceiverTest&&) = delete;
TlsReceiverTest(const TlsReceiverTest&) = delete;
TlsReceiverTest& operator=(TlsReceiverTest&&) = delete;
TlsReceiverTest& operator=(const TlsReceiverTest&) = delete;
Own<ConnectionReceiver> receiver;
MockConnectionReceiver* baseReceiver;
};
KJ_TEST("TLS receiver basics") {
TlsReceiverTest test;
auto clientPromise = test.baseReceiver->connect().then([&](auto stream) {
return test.tlsClient.wrapClient(kj::mv(stream), "example.com");
});
auto serverPromise = test.receiver->accept();
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
test.testConnection(*client, *server);
}
KJ_TEST("TLS receiver experiences pre-TLS error") {
TlsReceiverTest test;
KJ_LOG(INFO, "Accepting before a bad connect");
auto promise = test.receiver->accept();
KJ_LOG(INFO, "Disappointing our server");
test.baseReceiver->badConnect();
// Can't use KJ_EXPECT_THROW_RECOVERABLE_MESSAGE because wait() that returns a value can't throw
// recoverable exceptions. Can't use KJ_EXPECT_THROW_MESSAGE because non-recoverable exceptions
// will fork() in -fno-exception which screws up our state.
promise.then([](auto) {
KJ_FAIL_EXPECT("expected exception");
}, [](kj::Exception&& e) {
KJ_EXPECT(e.getDescription() == "Pipes are leaky");
}).wait(test.io.waitScope);
KJ_LOG(INFO, "Trying to load a promise after failure");
test.receiver->accept().then([](auto) {
KJ_FAIL_EXPECT("expected exception");
}, [](kj::Exception&& e) {
KJ_EXPECT(e.getDescription() == "Pipes are leaky");
}).wait(test.io.waitScope);
}
KJ_TEST("TLS receiver accepts multiple clients") {
TlsReceiverTest test;
auto wrapClient = [&](auto stream) {
return test.tlsClient.wrapClient(kj::mv(stream), "example.com");
};
auto writeToServer = [&](auto client) {
return test.writeToServer(*client).attach(kj::mv(client));
};
auto readFromClient = [&](auto server) {
return test.readFromClient(*server).attach(kj::mv(server));
};
KJ_LOG(INFO, "Requesting a bunch of client connects");
constexpr auto kClientCount = 20;
auto clientPromises = Vector<Promise<void>>();
for (auto i = 0; i < kClientCount; ++i) {
auto clientPromise = test.baseReceiver->connect().then(wrapClient).then(writeToServer);
clientPromises.add(kj::mv(clientPromise));
}
KJ_LOG(INFO, "Requesting and resolving a bunch of server accepts in sequence");
for (auto i = 0; i < kClientCount; ++i) {
// Resolve each receive in sequence like the Supervisor/Network.
test.receiver->accept().then(readFromClient).wait(test.io.waitScope);
}
KJ_LOG(INFO, "Resolving all of our client connects in parallel");
joinPromises(clientPromises.releaseAsArray()).wait(test.io.waitScope);
KJ_LOG(INFO, "Requesting one last server accept that we'll never resolve");
auto extraAcceptPromise = test.receiver->accept().then(readFromClient);
KJ_EXPECT(!extraAcceptPromise.poll(test.io.waitScope));
}
KJ_TEST("TLS receiver does not stall on client that disconnects before ssl handshake") {
TlsReceiverTest test;
auto wrapClient = [&](auto stream) {
return test.tlsClient.wrapClient(kj::mv(stream), "example.com");
};
auto writeToServer = [&](auto client) {
return test.writeToServer(*client).attach(kj::mv(client));
};
auto readFromClient = [&](auto server) {
return test.readFromClient(*server).attach(kj::mv(server));
};
constexpr auto kClientCount = 20;
auto clientPromises = Vector<Promise<void>>();
KJ_LOG(INFO, "Requesting the first batch of client connects in parallel");
for (auto i = 0; i < kClientCount / 2; ++i) {
auto clientPromise = test.baseReceiver->connect().then(wrapClient).then(writeToServer);
clientPromises.add(kj::mv(clientPromise));
}
KJ_LOG(INFO, "Requesting and resolving a client connect that hangs up before ssl connect");
KJ_ASSERT(test.baseReceiver->connect().wait(test.io.waitScope));
KJ_LOG(INFO, "Requesting the second batch of client connects in parallel");
for (auto i = 0; i < kClientCount / 2; ++i) {
auto clientPromise = test.baseReceiver->connect().then(wrapClient).then(writeToServer);
clientPromises.add(kj::mv(clientPromise));
}
KJ_LOG(INFO, "Requesting and resolving a bunch of server accepts in sequence");
for (auto i = 0; i < kClientCount; ++i) {
test.receiver->accept().then(readFromClient).wait(test.io.waitScope);
}
KJ_LOG(INFO, "Resolving all of our client connects in parallel");
joinPromises(clientPromises.releaseAsArray()).wait(test.io.waitScope);
KJ_LOG(INFO, "Requesting one last server accept that we'll never resolve");
auto extraAcceptPromise = test.receiver->accept().then(readFromClient);
KJ_EXPECT(!extraAcceptPromise.poll(test.io.waitScope));
}
KJ_TEST("TLS receiver does not stall on hung client") {
TlsReceiverTest test;
auto wrapClient = [&](auto stream) {
return test.tlsClient.wrapClient(kj::mv(stream), "example.com");
};
auto writeToServer = [&](auto client) {
return test.writeToServer(*client).attach(kj::mv(client));
};
auto readFromClient = [&](auto server) {
return test.readFromClient(*server).attach(kj::mv(server));
};
constexpr auto kClientCount = 20;
auto clientPromises = Vector<Promise<void>>();
KJ_LOG(INFO, "Requesting the first batch of client connects in parallel");
for (auto i = 0; i < kClientCount / 2; ++i) {
auto clientPromise = test.baseReceiver->connect().then(wrapClient).then(writeToServer);
clientPromises.add(kj::mv(clientPromise));
}
KJ_LOG(INFO, "Requesting and resolving a client connect that never does ssl connect");
auto hungClient = test.baseReceiver->connect().wait(test.io.waitScope);
KJ_ASSERT(hungClient);
KJ_LOG(INFO, "Requesting the second batch of client connects in parallel");
for (auto i = 0; i < kClientCount / 2; ++i) {
auto clientPromise = test.baseReceiver->connect().then(wrapClient).then(writeToServer);
clientPromises.add(kj::mv(clientPromise));
}
KJ_LOG(INFO, "Requesting and resolving a bunch of server accepts in sequence");
for (auto i = 0; i < kClientCount; ++i) {
test.receiver->accept().then(readFromClient).wait(test.io.waitScope);
}
KJ_LOG(INFO, "Resolving all of our client connects in parallel");
joinPromises(clientPromises.releaseAsArray()).wait(test.io.waitScope);
KJ_LOG(INFO, "Releasing the hung client");
hungClient = {};
KJ_LOG(INFO, "Requesting one last server accept that we'll never resolve");
auto extraAcceptPromise = test.receiver->accept().then(readFromClient);
KJ_EXPECT(!extraAcceptPromise.poll(test.io.waitScope));
}
#ifdef KJ_EXTERNAL_TESTS
KJ_TEST("TLS to capnproto.org") {
kj::AsyncIoContext io = setupAsyncIo();
TlsContext tls;
auto network = tls.wrapNetwork(io.provider->getNetwork());
auto addr = network->parseAddress("capnproto.org", 443).wait(io.waitScope);
auto stream = addr->connect().wait(io.waitScope);
kj::StringPtr request =
"HEAD / HTTP/1.1\r\n"
"Host: capnproto.org\r\n"
"Connection: close\r\n"
"User-Agent: capnp-test/0.6\r\n"
"\r\n";
stream->write(request.begin(), request.size()).wait(io.waitScope);
char buffer[4096];
size_t n = stream->tryRead(buffer, sizeof(buffer) - 1, sizeof(buffer) - 1).wait(io.waitScope);
buffer[n] = '\0';
kj::StringPtr response(buffer, n);
KJ_ASSERT(response.startsWith("HTTP/1.1 200 OK\r\n"));
}
#endif
} // namespace
} // namespace kj
#endif // KJ_HAS_OPENSSL