• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3# Copyright 2022 gRPC authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# Generator script for src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h and test/core/security/grpc_tls_credentials_options_comparator_test.cc
18# Should be executed from grpc's root directory.
19
20from __future__ import print_function
21
22import collections
23from dataclasses import dataclass
24import difflib
25import filecmp
26import os
27import sys
28import tempfile
29
30
31@dataclass
32class DataMember:
33    name: str  # name of the data member without the trailing '_'
34    type: str  # Type (eg. std::string, bool)
35    test_name: str  # The name to use for the associated test
36    test_value_1: str  # Test-specific value to use for comparison
37    test_value_2: str  # Test-specific value (different from test_value_1)
38    default_initializer: str = (  # If non-empty, this will be used as the default initialization of this field
39        ""
40    )
41    getter_comment: str = ""  # Comment to add before the getter for this field
42    special_getter_return_type: str = (  # Override for the return type of getter (eg. const std::string&)
43        ""
44    )
45    override_getter: str = (  # Override for the entire getter method. Relevant for certificate_verifier and certificate_provider
46        ""
47    )
48    setter_comment: str = ""  # Commend to add before the setter for this field
49    setter_move_semantics: bool = False  # Should the setter use move-semantics
50    special_comparator: str = (  # If non-empty, this will be used in `operator==`
51        ""
52    )
53
54
55_DATA_MEMBERS = [
56    DataMember(
57        name="cert_request_type",
58        type="grpc_ssl_client_certificate_request_type",
59        default_initializer="GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE",
60        test_name="DifferentCertRequestType",
61        test_value_1="GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE",
62        test_value_2="GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY",
63    ),
64    DataMember(
65        name="verify_server_cert",
66        type="bool",
67        default_initializer="true",
68        test_name="DifferentVerifyServerCert",
69        test_value_1="false",
70        test_value_2="true",
71    ),
72    DataMember(
73        name="min_tls_version",
74        type="grpc_tls_version",
75        default_initializer="grpc_tls_version::TLS1_2",
76        test_name="DifferentMinTlsVersion",
77        test_value_1="grpc_tls_version::TLS1_2",
78        test_value_2="grpc_tls_version::TLS1_3",
79    ),
80    DataMember(
81        name="max_tls_version",
82        type="grpc_tls_version",
83        default_initializer="grpc_tls_version::TLS1_3",
84        test_name="DifferentMaxTlsVersion",
85        test_value_1="grpc_tls_version::TLS1_2",
86        test_value_2="grpc_tls_version::TLS1_3",
87    ),
88    DataMember(
89        name="certificate_verifier",
90        type="grpc_core::RefCountedPtr<grpc_tls_certificate_verifier>",
91        override_getter="""grpc_tls_certificate_verifier* certificate_verifier() {
92    return certificate_verifier_.get();
93  }""",
94        setter_move_semantics=True,
95        special_comparator=(
96            "(certificate_verifier_ == other.certificate_verifier_ ||"
97            " (certificate_verifier_ != nullptr && other.certificate_verifier_"
98            " != nullptr &&"
99            " certificate_verifier_->Compare(other.certificate_verifier_.get())"
100            " == 0))"
101        ),
102        test_name="DifferentCertificateVerifier",
103        test_value_1="MakeRefCounted<HostNameCertificateVerifier>()",
104        test_value_2="MakeRefCounted<XdsCertificateVerifier>(nullptr)",
105    ),
106    DataMember(
107        name="check_call_host",
108        type="bool",
109        default_initializer="true",
110        test_name="DifferentCheckCallHost",
111        test_value_1="false",
112        test_value_2="true",
113    ),
114    DataMember(
115        name="certificate_provider",
116        type="grpc_core::RefCountedPtr<grpc_tls_certificate_provider>",
117        getter_comment=(
118            "Returns the distributor from certificate_provider_ if it is set,"
119            " nullptr otherwise."
120        ),
121        override_getter="""grpc_tls_certificate_distributor* certificate_distributor() {
122    if (certificate_provider_ != nullptr) { return certificate_provider_->distributor().get(); }
123    return nullptr;
124  }""",
125        setter_move_semantics=True,
126        special_comparator=(
127            "(certificate_provider_ == other.certificate_provider_ ||"
128            " (certificate_provider_ != nullptr && other.certificate_provider_"
129            " != nullptr &&"
130            " certificate_provider_->Compare(other.certificate_provider_.get())"
131            " == 0))"
132        ),
133        test_name="DifferentCertificateProvider",
134        test_value_1=(
135            'MakeRefCounted<StaticDataCertificateProvider>("root_cert_1",'
136            " PemKeyCertPairList())"
137        ),
138        test_value_2=(
139            'MakeRefCounted<StaticDataCertificateProvider>("root_cert_2",'
140            " PemKeyCertPairList())"
141        ),
142    ),
143    DataMember(
144        name="watch_root_cert",
145        type="bool",
146        default_initializer="false",
147        setter_comment=(
148            "If need to watch the updates of root certificates with name"
149            " |root_cert_name|. The default value is false. If used in"
150            " tls_credentials, it should always be set to true unless the root"
151            " certificates are not needed."
152        ),
153        test_name="DifferentWatchRootCert",
154        test_value_1="false",
155        test_value_2="true",
156    ),
157    DataMember(
158        name="root_cert_name",
159        type="std::string",
160        special_getter_return_type="const std::string&",
161        setter_comment=(
162            "Sets the name of root certificates being watched, if"
163            " |set_watch_root_cert| is called. If not set, an empty string will"
164            " be used as the name."
165        ),
166        setter_move_semantics=True,
167        test_name="DifferentRootCertName",
168        test_value_1='"root_cert_name_1"',
169        test_value_2='"root_cert_name_2"',
170    ),
171    DataMember(
172        name="watch_identity_pair",
173        type="bool",
174        default_initializer="false",
175        setter_comment=(
176            "If need to watch the updates of identity certificates with name"
177            " |identity_cert_name|. The default value is false. If used in"
178            " tls_credentials, it should always be set to true unless the"
179            " identity key-cert pairs are not needed."
180        ),
181        test_name="DifferentWatchIdentityPair",
182        test_value_1="false",
183        test_value_2="true",
184    ),
185    DataMember(
186        name="identity_cert_name",
187        type="std::string",
188        special_getter_return_type="const std::string&",
189        setter_comment=(
190            "Sets the name of identity key-cert pairs being watched, if"
191            " |set_watch_identity_pair| is called. If not set, an empty string"
192            " will be used as the name."
193        ),
194        setter_move_semantics=True,
195        test_name="DifferentIdentityCertName",
196        test_value_1='"identity_cert_name_1"',
197        test_value_2='"identity_cert_name_2"',
198    ),
199    DataMember(
200        name="tls_session_key_log_file_path",
201        type="std::string",
202        special_getter_return_type="const std::string&",
203        setter_move_semantics=True,
204        test_name="DifferentTlsSessionKeyLogFilePath",
205        test_value_1='"file_path_1"',
206        test_value_2='"file_path_2"',
207    ),
208    DataMember(
209        name="crl_directory",
210        type="std::string",
211        special_getter_return_type="const std::string&",
212        setter_comment=(
213            " gRPC will enforce CRLs on all handshakes from all hashed CRL"
214            " files inside of the crl_directory. If not set, an empty string"
215            " will be used, which will not enable CRL checking. Only supported"
216            " for OpenSSL version > 1.1."
217        ),
218        setter_move_semantics=True,
219        test_name="DifferentCrlDirectory",
220        test_value_1='"crl_directory_1"',
221        test_value_2='"crl_directory_2"',
222    ),
223    DataMember(
224        name="crl_provider",
225        type="std::shared_ptr<grpc_core::experimental::CrlProvider>",
226        getter_comment=("Returns the CRL Provider"),
227        setter_move_semantics=True,
228        special_comparator=("(crl_provider_ == other.crl_provider_)"),
229        test_name="DifferentCrlProvider",
230        test_value_1=("*experimental::CreateStaticCrlProvider({})"),
231        test_value_2=("*experimental::CreateStaticCrlProvider({})"),
232    ),
233    DataMember(
234        name="send_client_ca_list",
235        type="bool",
236        default_initializer="false",
237        test_name="DifferentSendClientCaListValues",
238        test_value_1="false",
239        test_value_2="true",
240    ),
241]
242
243
244# print copyright notice from this file
245def put_copyright(f, year):
246    print(
247        """//
248//
249// Copyright %s gRPC authors.
250//
251// Licensed under the Apache License, Version 2.0 (the "License");
252// you may not use this file except in compliance with the License.
253// You may obtain a copy of the License at
254//
255//     http://www.apache.org/licenses/LICENSE-2.0
256//
257// Unless required by applicable law or agreed to in writing, software
258// distributed under the License is distributed on an "AS IS" BASIS,
259// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
260// See the License for the specific language governing permissions and
261// limitations under the License.
262//
263//
264"""
265        % (year),
266        file=f,
267    )
268
269
270# Prints differences between two files
271def get_file_differences(file1, file2):
272    with open(file1) as f1:
273        file1_text = f1.readlines()
274    with open(file2) as f2:
275        file2_text = f2.readlines()
276    return difflib.unified_diff(
277        file1_text, file2_text, fromfile=file1, tofile=file2
278    )
279
280
281# Is this script executed in test mode?
282test_mode = False
283if len(sys.argv) > 1 and sys.argv[1] == "--test":
284    test_mode = True
285
286HEADER_FILE_NAME = (
287    "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"
288)
289# Generate src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
290header_file_name = HEADER_FILE_NAME
291if test_mode:
292    header_file_name = tempfile.NamedTemporaryFile(delete=False).name
293H = open(header_file_name, "w")
294
295put_copyright(H, "2018")
296print(
297    "// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py\n",
298    file=H,
299)
300print(
301    """#ifndef GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H
302#define GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H
303
304#include <grpc/support/port_platform.h>
305
306#include "absl/container/inlined_vector.h"
307
308#include <grpc/credentials.h>
309#include <grpc/grpc_security.h>
310
311#include "src/core/util/ref_counted.h"
312#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h"
313#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
314#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_verifier.h"
315#include "src/core/lib/security/security_connector/ssl_utils.h"
316
317// Contains configurable options specified by callers to configure their certain
318// security features supported in TLS.
319// TODO(ZhenLian): consider making this not ref-counted.
320struct grpc_tls_credentials_options
321    : public grpc_core::RefCounted<grpc_tls_credentials_options> {
322 public:
323  grpc_tls_credentials_options() = default;
324  ~grpc_tls_credentials_options() override = default;
325""",
326    file=H,
327)
328
329# Print out getters for all data members
330print("  // Getters for member fields.", file=H)
331for data_member in _DATA_MEMBERS:
332    if data_member.getter_comment != "":
333        print("  // " + data_member.getter_comment, file=H)
334    if data_member.override_getter:
335        print("  " + data_member.override_getter, file=H)
336    else:
337        print(
338            "  %s %s() const { return %s; }"
339            % (
340                data_member.special_getter_return_type
341                if data_member.special_getter_return_type != ""
342                else data_member.type,
343                data_member.name,
344                data_member.name + "_",
345            ),
346            file=H,
347        )
348
349# Print out setters for all data members
350print("", file=H)
351print("  // Setters for member fields.", file=H)
352for data_member in _DATA_MEMBERS:
353    if data_member.setter_comment != "":
354        print("  // " + data_member.setter_comment, file=H)
355    if data_member.setter_move_semantics:
356        print(
357            "  void set_%s(%s %s) { %s_ = std::move(%s); }"
358            % (
359                data_member.name,
360                data_member.type,
361                data_member.name,
362                data_member.name,
363                data_member.name,
364            ),
365            file=H,
366        )
367    else:
368        print(
369            "  void set_%s(%s %s) { %s_ = %s; }"
370            % (
371                data_member.name,
372                data_member.type,
373                data_member.name,
374                data_member.name,
375                data_member.name,
376            ),
377            file=H,
378        )
379
380# Write out operator==
381print(
382    "\n  bool operator==(const grpc_tls_credentials_options& other) const {",
383    file=H,
384)
385operator_equal_content = "    return "
386for i in range(len(_DATA_MEMBERS)):
387    if i != 0:
388        operator_equal_content += "      "
389    if _DATA_MEMBERS[i].special_comparator != "":
390        operator_equal_content += _DATA_MEMBERS[i].special_comparator
391    else:
392        operator_equal_content += (
393            _DATA_MEMBERS[i].name + "_ == other." + _DATA_MEMBERS[i].name + "_"
394        )
395    if i != len(_DATA_MEMBERS) - 1:
396        operator_equal_content += " &&\n"
397print(operator_equal_content + ";\n  }", file=H)
398
399# Write out copy constructor
400print(
401    "\n  grpc_tls_credentials_options(grpc_tls_credentials_options& other) :",
402    file=H,
403)
404operator_equal_content = "      "
405for i in range(len(_DATA_MEMBERS)):
406    if i != 0:
407        operator_equal_content += "      "
408    if i == len(_DATA_MEMBERS) - 1:
409        operator_equal_content += (
410            _DATA_MEMBERS[i].name + "_(other." + _DATA_MEMBERS[i].name + "_)"
411        )
412    else:
413        operator_equal_content += (
414            _DATA_MEMBERS[i].name + "_(other." + _DATA_MEMBERS[i].name + "_),\n"
415        )
416print(operator_equal_content + "  {}", file=H)
417
418# Print out data member declarations
419print("\n private:", file=H)
420for data_member in _DATA_MEMBERS:
421    if data_member.default_initializer == "":
422        print(
423            "  %s %s_;"
424            % (
425                data_member.type,
426                data_member.name,
427            ),
428            file=H,
429        )
430    else:
431        print(
432            "  %s %s_ = %s;"
433            % (
434                data_member.type,
435                data_member.name,
436                data_member.default_initializer,
437            ),
438            file=H,
439        )
440
441# Print out file ending
442print(
443    """};
444
445#endif  // GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H""",
446    file=H,
447)
448
449H.close()
450
451# Generate test/core/security/grpc_tls_credentials_options_comparator_test.cc
452TEST_FILE_NAME = (
453    "test/core/security/grpc_tls_credentials_options_comparator_test.cc"
454)
455test_file_name = TEST_FILE_NAME
456if test_mode:
457    test_file_name = tempfile.NamedTemporaryFile(delete=False).name
458T = open(test_file_name, "w")
459
460put_copyright(T, "2022")
461print(
462    "// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py",
463    file=T,
464)
465print(
466    """
467#include <grpc/support/port_platform.h>
468
469#include <string>
470
471#include <gmock/gmock.h>
472
473#include <grpc/credentials.h>
474
475#include "src/core/lib/security/credentials/xds/xds_credentials.h"
476#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"
477#include "test/core/test_util/test_config.h"
478
479namespace grpc_core {
480namespace {
481""",
482    file=T,
483)
484
485# Generate negative test for each negative member
486for data_member in _DATA_MEMBERS:
487    print(
488        """TEST(TlsCredentialsOptionsComparatorTest, %s) {
489  auto* options_1 = grpc_tls_credentials_options_create();
490  auto* options_2 = grpc_tls_credentials_options_create();
491  options_1->set_%s(%s);
492  options_2->set_%s(%s);
493  EXPECT_FALSE(*options_1 == *options_2);
494  EXPECT_FALSE(*options_2 == *options_1);
495  delete options_1;
496  delete options_2;
497}"""
498        % (
499            data_member.test_name,
500            data_member.name,
501            data_member.test_value_1,
502            data_member.name,
503            data_member.test_value_2,
504        ),
505        file=T,
506    )
507
508# Print out file ending
509print(
510    """
511} // namespace
512} // namespace grpc_core
513
514int main(int argc, char** argv) {
515  testing::InitGoogleTest(&argc, argv);
516  grpc::testing::TestEnvironment env(&argc, argv);
517  grpc_init();
518  auto result = RUN_ALL_TESTS();
519  grpc_shutdown();
520  return result;
521}""",
522    file=T,
523)
524T.close()
525
526if test_mode:
527    header_diff = get_file_differences(header_file_name, HEADER_FILE_NAME)
528    test_diff = get_file_differences(test_file_name, TEST_FILE_NAME)
529    os.unlink(header_file_name)
530    os.unlink(test_file_name)
531    header_error = False
532    for line in header_diff:
533        print(line)
534        header_error = True
535    if header_error:
536        print(
537            HEADER_FILE_NAME
538            + " should not be manually modified. Please make changes to"
539            " tools/distrib/gen_grpc_tls_credentials_options.py instead."
540        )
541    test_error = False
542    for line in test_diff:
543        print(line)
544        test_error = True
545    if test_error:
546        print(
547            TEST_FILE_NAME
548            + " should not be manually modified. Please make changes to"
549            " tools/distrib/gen_grpc_tls_credentials_options.py instead."
550        )
551    if header_error or test_error:
552        sys.exit(1)
553