// Copyright 2021 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_tls_client/test/test_server.h" #include #include #include "gtest/gtest.h" // The following header contains a set of test certificates and keys. // It is generated by // third_party/boringssl/py/boringssl/generate_test_data.py. #include "test_certs_and_keys.h" #define ASSERT_OK(expr) ASSERT_EQ(pw::OkStatus(), expr) namespace pw::tls_client::test { namespace { int TestClientBioRead(BIO* bio, char* out, int outl) { auto read_writer = static_cast(bio->ptr); auto res = read_writer->Read(out, outl); if (!res.ok()) { return -1; } if (res.value().size() == 0) { BIO_set_retry_read(bio); return -1; } return res.value().size(); } int TestClientBioWrite(BIO* bio, const char* in, int inl) { auto read_writer = static_cast(bio->ptr); auto res = read_writer->Write(in, inl); if (!res.ok()) { return -1; } return inl; } int TestClientBioNew(BIO* bio) { bio->init = 1; return 1; } long TestClientBioCtrl(BIO*, int, long, void*) { return 1; } int TestClientBioFree(BIO*) { return 1; } const BIO_METHOD bio_method = { BIO_TYPE_MEM, "bio test server test", TestClientBioWrite, TestClientBioRead, nullptr, nullptr, TestClientBioCtrl, TestClientBioNew, TestClientBioFree, nullptr, }; // Server needs to send certificate. Thus the send buffer needs to be bigger. std::array server_send_buffer; std::array server_receive_buffer; // Create a raw BoringSSL client and load test trust anchors. void CreateSSLClient(bssl::UniquePtr* ctx, bssl::UniquePtr* client, stream::ReaderWriter* read_writer) { *ctx = bssl::UniquePtr(SSL_CTX_new(TLS_method())); ASSERT_NE(*ctx, nullptr); *client = bssl::UniquePtr(SSL_new(ctx->get())); ASSERT_NE(*client, nullptr); BIO* bio = BIO_new(&bio_method); ASSERT_NE(bio, nullptr); // Load trust anchors to client auto store = SSL_CTX_get_cert_store(ctx->get()); X509_VERIFY_PARAM_clear_flags(store->param, X509_V_FLAG_USE_CHECK_TIME); const pw::ConstByteSpan kTrustAnchors[] = {kRootACert, kRootBCert}; for (auto cert : kTrustAnchors) { auto res = ParseDerCertificate(cert); ASSERT_OK(res.status()); ASSERT_EQ(X509_STORE_add_cert(store, res.value()), 1); X509_free(res.value()); } bio->ptr = read_writer; SSL_set_bio(client->get(), bio, bio); } } // namespace TEST(InMemoryTestServer, NormalConnectionSucceed) { InMemoryTestServer server(server_receive_buffer, server_send_buffer); const ConstByteSpan kIntermediates[] = {kSubCACert}; ASSERT_OK(server.Initialize(kServerKey, kServerCert, kIntermediates)); // Create a raw BoringSSL client bssl::UniquePtr client_ctx; bssl::UniquePtr ssl_client; CreateSSLClient(&client_ctx, &ssl_client, &server); // Handshake should be OK ASSERT_EQ(SSL_connect(ssl_client.get()), 1); ASSERT_TRUE(server.SessionEstablished()); // Client should pass certificate verification. ASSERT_EQ(SSL_get_verify_result(ssl_client.get()), 0); // Send some data to server const char send_expected[] = "hello"; int send_len = SSL_write(ssl_client.get(), send_expected, sizeof(send_expected)); ASSERT_EQ(static_cast(send_len), sizeof(send_expected)); char receive_actual[sizeof(send_expected) + 1] = {0}; int read_ret = SSL_read(ssl_client.get(), receive_actual, sizeof(receive_actual)); ASSERT_EQ(static_cast(read_ret), sizeof(send_expected)); ASSERT_STREQ(send_expected, receive_actual); // Shutdown EXPECT_FALSE(server.ClientShutdownReceived()); ASSERT_NE(SSL_shutdown(ssl_client.get()), -1); ASSERT_TRUE(server.ClientShutdownReceived()); } TEST(InMemoryTestServer, BufferTooSmallErrorsOut) { std::array insufficient_buffer; InMemoryTestServer server(server_receive_buffer, insufficient_buffer); const ConstByteSpan kIntermediates[] = {kSubCACert}; ASSERT_OK(server.Initialize(kServerKey, kServerCert, kIntermediates)); // Create a raw BoringSSL client bssl::UniquePtr client_ctx; bssl::UniquePtr ssl_client; CreateSSLClient(&client_ctx, &ssl_client, &server); // Handshake should fail as server shouldn't have enough buffer ASSERT_NE(SSL_connect(ssl_client.get()), 1); ASSERT_EQ(server.GetLastBioStatus(), Status::ResourceExhausted()); } } // namespace pw::tls_client::test