xref: /aosp_15_r20/external/grpc-grpc/test/cpp/end2end/tls_key_export_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <memory>
16 #include <string>
17 #include <thread>  // NOLINT
18 #include <vector>
19 
20 #include "absl/strings/str_cat.h"
21 #include "absl/strings/str_split.h"
22 #include "absl/strings/string_view.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 
26 #include <grpc++/grpc++.h>
27 #include <grpc/grpc.h>
28 #include <grpc/grpc_security.h>
29 #include <grpcpp/security/server_credentials.h>
30 #include <grpcpp/security/tls_credentials_options.h>
31 #include <grpcpp/support/channel_arguments.h>
32 
33 #include "src/core/lib/gpr/tmpfile.h"
34 #include "src/cpp/client/secure_credentials.h"
35 #include "src/proto/grpc/testing/echo.grpc.pb.h"
36 #include "test/core/util/resolve_localhost_ip46.h"
37 #include "test/core/util/test_config.h"
38 #include "test/core/util/tls_utils.h"
39 
40 extern "C" {
41 #include <openssl/ssl.h>
42 }
43 
44 #if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
45 #define TLS_KEY_LOGGING_AVAILABLE
46 #endif
47 
48 #define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
49 #define SERVER_KEY_PATH "src/core/tsi/test_creds/server0.key"
50 #define SERVER_CERT_PATH "src/core/tsi/test_creds/server0.pem"
51 #define CLIENT_KEY_PATH "src/core/tsi/test_creds/client.key"
52 #define CLIENT_CERT_PATH "src/core/tsi/test_creds/client.pem"
53 
54 #define NUM_REQUESTS_PER_CHANNEL 5
55 
56 using ::grpc::experimental::FileWatcherCertificateProvider;
57 using ::grpc::experimental::TlsChannelCredentialsOptions;
58 using ::grpc::experimental::TlsServerCredentialsOptions;
59 
60 // TODO(gtcooke94) - Tests current failing with OpenSSL 1.1.1 and 3.0. Fix and
61 // re-enable.
62 #ifdef OPENSSL_IS_BORINGSSL
63 
64 namespace grpc {
65 namespace testing {
66 namespace {
67 
68 class EchoServer final : public EchoTestService::Service {
Echo(grpc::ServerContext *,const EchoRequest * request,EchoResponse * response)69   grpc::Status Echo(grpc::ServerContext* /*context*/,
70                     const EchoRequest* request,
71                     EchoResponse* response) override {
72     if (request->param().expected_error().code() == 0) {
73       response->set_message(request->message());
74       return grpc::Status::OK;
75     } else {
76       return grpc::Status(static_cast<grpc::StatusCode>(
77                               request->param().expected_error().code()),
78                           "");
79     }
80   }
81 };
82 
83 class TestScenario {
84  public:
TestScenario(int num_listening_ports,bool share_tls_key_log_file,bool enable_tls_key_logging)85   TestScenario(int num_listening_ports, bool share_tls_key_log_file,
86                bool enable_tls_key_logging)
87       : num_listening_ports_(num_listening_ports),
88         share_tls_key_log_file_(share_tls_key_log_file),
89         enable_tls_key_logging_(enable_tls_key_logging) {}
AsString() const90   std::string AsString() const {
91     return absl::StrCat("TestScenario__num_listening_ports_",
92                         num_listening_ports_, "__share_tls_key_log_file_",
93                         (share_tls_key_log_file_ ? "true" : "false"),
94                         "__enable_tls_key_logging_",
95                         (enable_tls_key_logging_ ? "true" : "false"));
96   }
97 
num_listening_ports() const98   int num_listening_ports() const { return num_listening_ports_; }
99 
share_tls_key_log_file() const100   bool share_tls_key_log_file() const { return share_tls_key_log_file_; }
101 
enable_tls_key_logging() const102   bool enable_tls_key_logging() const { return enable_tls_key_logging_; }
103 
104  private:
105   int num_listening_ports_;
106   bool share_tls_key_log_file_;
107   bool enable_tls_key_logging_;
108 };
109 
TestScenarioName(const::testing::TestParamInfo<TestScenario> & info)110 std::string TestScenarioName(
111     const ::testing::TestParamInfo<TestScenario>& info) {
112   return info.param.AsString();
113 }
114 
CountOccurrencesInFileContents(std::string file_contents,std::string search_string)115 int CountOccurrencesInFileContents(std::string file_contents,
116                                    std::string search_string) {
117   int occurrences = 0;
118   std::string::size_type pos = 0;
119   while ((pos = file_contents.find(search_string, pos)) != std::string::npos) {
120     ++occurrences;
121     pos += search_string.length();
122   }
123   return occurrences;
124 }
125 
126 class TlsKeyLoggingEnd2EndTest : public ::testing::TestWithParam<TestScenario> {
127  protected:
CreateTmpFile()128   std::string CreateTmpFile() {
129     char* name = nullptr;
130     FILE* file_descriptor = gpr_tmpfile("GrpcTlsKeyLoggerTest", &name);
131     GPR_ASSERT(fclose(file_descriptor) == 0);
132     GPR_ASSERT(file_descriptor != nullptr);
133     GPR_ASSERT(name != nullptr);
134     std::string name_to_return = name;
135     gpr_free(name);
136     return name_to_return;
137   }
138 
SetUp()139   void SetUp() override {
140     grpc::ServerBuilder builder;
141     grpc::ChannelArguments args;
142     args.SetSslTargetNameOverride("foo.test.google.com.au");
143 
144     if (GetParam().num_listening_ports() > 0) {
145       ports_.resize(GetParam().num_listening_ports(), 0);
146     }
147 
148     std::string shared_key_log_file_server;
149     std::string shared_key_log_file_channel;
150 
151     if (GetParam().share_tls_key_log_file()) {
152       shared_key_log_file_server = CreateTmpFile();
153       shared_key_log_file_channel = CreateTmpFile();
154     }
155 
156     auto server_certificate_provider =
157         std::make_shared<FileWatcherCertificateProvider>(
158             SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
159 
160     auto channel_certificate_provider =
161         std::make_shared<FileWatcherCertificateProvider>(
162             CLIENT_KEY_PATH, CLIENT_CERT_PATH, CA_CERT_PATH, 1);
163 
164     for (int i = 0; i < GetParam().num_listening_ports(); i++) {
165       // Configure tls credential options for each port
166       TlsServerCredentialsOptions server_creds_options(
167           server_certificate_provider);
168       server_creds_options.set_cert_request_type(
169           GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
170       server_creds_options.watch_identity_key_cert_pairs();
171       server_creds_options.watch_root_certs();
172 
173       // Set a separate ssl key log file for each port if not shared
174       if (GetParam().share_tls_key_log_file()) {
175         tmp_server_tls_key_log_file_by_port_.push_back(
176             shared_key_log_file_server);
177       } else {
178         tmp_server_tls_key_log_file_by_port_.push_back(CreateTmpFile());
179       }
180 
181       if (GetParam().enable_tls_key_logging()) {
182         server_creds_options.set_tls_session_key_log_file_path(
183             tmp_server_tls_key_log_file_by_port_[i]);
184       }
185 
186       builder.AddListeningPort(
187           "0.0.0.0:0",
188           grpc::experimental::TlsServerCredentials(server_creds_options),
189           &ports_[i]);
190     }
191 
192     builder.RegisterService(&service_);
193     server_ = builder.BuildAndStart();
194     ASSERT_NE(nullptr, server_);
195     server_thread_ =
196         std::thread(&TlsKeyLoggingEnd2EndTest::RunServerLoop, this);
197 
198     for (int i = 0; i < GetParam().num_listening_ports(); i++) {
199       ASSERT_NE(0, ports_[i]);
200       server_addresses_.push_back(grpc_core::LocalIpAndPort(ports_[i]));
201 
202       // Configure tls credential options for each stub. Each stub connects to
203       // a separate port on the server.
204       TlsChannelCredentialsOptions channel_creds_options;
205       channel_creds_options.set_certificate_provider(
206           channel_certificate_provider);
207       channel_creds_options.watch_identity_key_cert_pairs();
208       channel_creds_options.watch_root_certs();
209 
210       // Set a separate ssl key log file for each port if not shared.
211       if (GetParam().share_tls_key_log_file()) {
212         tmp_stub_tls_key_log_file_.push_back(shared_key_log_file_channel);
213       } else {
214         tmp_stub_tls_key_log_file_.push_back(CreateTmpFile());
215       }
216 
217       if (GetParam().enable_tls_key_logging()) {
218         channel_creds_options.set_tls_session_key_log_file_path(
219             tmp_stub_tls_key_log_file_[i]);
220       }
221 
222       stubs_.push_back(EchoTestService::NewStub(grpc::CreateCustomChannel(
223           server_addresses_[i],
224           grpc::experimental::TlsCredentials(channel_creds_options), args)));
225     }
226   }
227 
TearDown()228   void TearDown() override {
229     server_->Shutdown();
230     server_thread_.join();
231 
232     // Remove all created files.
233     for (int i = 0; i < GetParam().num_listening_ports(); i++) {
234       remove(tmp_stub_tls_key_log_file_[i].c_str());
235       remove(tmp_server_tls_key_log_file_by_port_[i].c_str());
236       if (GetParam().share_tls_key_log_file()) {
237         break;
238       }
239     }
240   }
241 
RunServerLoop()242   void RunServerLoop() { server_->Wait(); }
243 
244   const std::string client_method_name_ = "grpc.testing.EchoTestService/Echo";
245   const std::string server_method_name_ = "grpc.testing.EchoTestService/Echo";
246 
247   std::vector<int> ports_;
248   std::vector<std::string> tmp_server_tls_key_log_file_by_port_;
249   std::vector<std::string> tmp_stub_tls_key_log_file_;
250   std::vector<std::string> server_addresses_;
251   std::vector<std::unique_ptr<EchoTestService::Stub>> stubs_;
252   EchoServer service_;
253   std::unique_ptr<grpc::Server> server_;
254   std::thread server_thread_;
255 };
256 
TEST_P(TlsKeyLoggingEnd2EndTest,KeyLogging)257 TEST_P(TlsKeyLoggingEnd2EndTest, KeyLogging) {
258   // Cover all valid statuses.
259   for (int i = 0; i <= NUM_REQUESTS_PER_CHANNEL; ++i) {
260     for (int j = 0; j < GetParam().num_listening_ports(); ++j) {
261       EchoRequest request;
262       request.set_message("foo");
263       request.mutable_param()->mutable_expected_error()->set_code(0);
264       EchoResponse response;
265       grpc::ClientContext context;
266       grpc::Status status = stubs_[j]->Echo(&context, request, &response);
267       EXPECT_TRUE(status.ok());
268     }
269   }
270 
271   for (int i = 0; i < GetParam().num_listening_ports(); i++) {
272     std::string server_key_log = grpc_core::testing::GetFileContents(
273         tmp_server_tls_key_log_file_by_port_[i].c_str());
274     std::string channel_key_log = grpc_core::testing::GetFileContents(
275         tmp_stub_tls_key_log_file_[i].c_str());
276 
277     if (!GetParam().enable_tls_key_logging()) {
278       EXPECT_THAT(server_key_log, ::testing::IsEmpty());
279       EXPECT_THAT(channel_key_log, ::testing::IsEmpty());
280     }
281 
282 #ifdef TLS_KEY_LOGGING_AVAILABLE
283     std::vector<absl::string_view> server_separated =
284         absl::StrSplit(server_key_log, '\r');
285     std::vector<absl::string_view> client_separated =
286         absl::StrSplit(channel_key_log, '\r');
287     EXPECT_THAT(server_separated,
288                 ::testing::UnorderedElementsAreArray(client_separated));
289 
290     if (GetParam().share_tls_key_log_file() &&
291         GetParam().enable_tls_key_logging()) {
292       EXPECT_EQ(CountOccurrencesInFileContents(
293                     server_key_log, "CLIENT_HANDSHAKE_TRAFFIC_SECRET"),
294                 GetParam().num_listening_ports());
295       EXPECT_EQ(CountOccurrencesInFileContents(
296                     server_key_log, "SERVER_HANDSHAKE_TRAFFIC_SECRET"),
297                 GetParam().num_listening_ports());
298       EXPECT_EQ(CountOccurrencesInFileContents(server_key_log,
299                                                "CLIENT_TRAFFIC_SECRET_0"),
300                 GetParam().num_listening_ports());
301       EXPECT_EQ(CountOccurrencesInFileContents(server_key_log,
302                                                "SERVER_TRAFFIC_SECRET_0"),
303                 GetParam().num_listening_ports());
304       EXPECT_EQ(
305           CountOccurrencesInFileContents(server_key_log, "EXPORTER_SECRET"),
306           GetParam().num_listening_ports());
307     } else if (GetParam().enable_tls_key_logging()) {
308       EXPECT_EQ(CountOccurrencesInFileContents(
309                     server_key_log, "CLIENT_HANDSHAKE_TRAFFIC_SECRET"),
310                 1);
311       EXPECT_EQ(CountOccurrencesInFileContents(
312                     server_key_log, "SERVER_HANDSHAKE_TRAFFIC_SECRET"),
313                 1);
314       EXPECT_EQ(CountOccurrencesInFileContents(server_key_log,
315                                                "CLIENT_TRAFFIC_SECRET_0"),
316                 1);
317       EXPECT_EQ(CountOccurrencesInFileContents(server_key_log,
318                                                "SERVER_TRAFFIC_SECRET_0"),
319                 1);
320       EXPECT_EQ(
321           CountOccurrencesInFileContents(server_key_log, "EXPORTER_SECRET"), 1);
322     }
323 #else
324     // If TLS Key logging is not available, the files should be empty.
325     if (GetParam().enable_tls_key_logging()) {
326       EXPECT_THAT(server_key_log, ::testing::IsEmpty());
327       EXPECT_THAT(channel_key_log, ::testing::IsEmpty());
328     }
329 #endif
330 
331     if (GetParam().share_tls_key_log_file()) {
332       break;
333     }
334   }
335 }
336 
337 INSTANTIATE_TEST_SUITE_P(TlsKeyLogging, TlsKeyLoggingEnd2EndTest,
338                          ::testing::ValuesIn({TestScenario(5, false, true),
339                                               TestScenario(5, true, true),
340                                               TestScenario(5, true, false),
341                                               TestScenario(5, false, false)}),
342                          &TestScenarioName);
343 
344 }  // namespace
345 }  // namespace testing
346 }  // namespace grpc
347 
348 #endif  // OPENSSL_IS_BORING_SSL
349 
main(int argc,char ** argv)350 int main(int argc, char** argv) {
351   ::testing::InitGoogleTest(&argc, argv);
352   grpc::testing::TestEnvironment env(&argc, argv);
353   return RUN_ALL_TESTS();
354 }
355