quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

generate_tls13_compat_tests.py (24294B)


      1 #!/usr/bin/env python3
      2 
      3 # generate_tls13_compat_tests.py
      4 #
      5 # Copyright The Mbed TLS Contributors
      6 # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
      7 
      8 """
      9 Generate TLSv1.3 Compat test cases
     10 
     11 """
     12 
     13 import sys
     14 import os
     15 import argparse
     16 import itertools
     17 from collections import namedtuple
     18 
     19 # define certificates configuration entry
     20 Certificate = namedtuple("Certificate", ['cafile', 'certfile', 'keyfile'])
     21 # define the certificate parameters for signature algorithms
     22 CERTIFICATES = {
     23     'ecdsa_secp256r1_sha256': Certificate('$DATA_FILES_PATH/test-ca2.crt',
     24                                           '$DATA_FILES_PATH/ecdsa_secp256r1.crt',
     25                                           '$DATA_FILES_PATH/ecdsa_secp256r1.key'),
     26     'ecdsa_secp384r1_sha384': Certificate('$DATA_FILES_PATH/test-ca2.crt',
     27                                           '$DATA_FILES_PATH/ecdsa_secp384r1.crt',
     28                                           '$DATA_FILES_PATH/ecdsa_secp384r1.key'),
     29     'ecdsa_secp521r1_sha512': Certificate('$DATA_FILES_PATH/test-ca2.crt',
     30                                           '$DATA_FILES_PATH/ecdsa_secp521r1.crt',
     31                                           '$DATA_FILES_PATH/ecdsa_secp521r1.key'),
     32     'rsa_pss_rsae_sha256': Certificate('$DATA_FILES_PATH/test-ca_cat12.crt',
     33                                        '$DATA_FILES_PATH/server2-sha256.crt',
     34                                        '$DATA_FILES_PATH/server2.key')
     35 }
     36 
     37 CIPHER_SUITE_IANA_VALUE = {
     38     "TLS_AES_128_GCM_SHA256": 0x1301,
     39     "TLS_AES_256_GCM_SHA384": 0x1302,
     40     "TLS_CHACHA20_POLY1305_SHA256": 0x1303,
     41     "TLS_AES_128_CCM_SHA256": 0x1304,
     42     "TLS_AES_128_CCM_8_SHA256": 0x1305
     43 }
     44 
     45 SIG_ALG_IANA_VALUE = {
     46     "ecdsa_secp256r1_sha256": 0x0403,
     47     "ecdsa_secp384r1_sha384": 0x0503,
     48     "ecdsa_secp521r1_sha512": 0x0603,
     49     'rsa_pss_rsae_sha256': 0x0804,
     50 }
     51 
     52 NAMED_GROUP_IANA_VALUE = {
     53     'secp256r1': 0x17,
     54     'secp384r1': 0x18,
     55     'secp521r1': 0x19,
     56     'x25519': 0x1d,
     57     'x448': 0x1e,
     58     # Only one finite field group to keep testing time within reasonable bounds.
     59     'ffdhe2048': 0x100,
     60 }
     61 
     62 class TLSProgram:
     63     """
     64     Base class for generate server/client command.
     65     """
     66 
     67     # pylint: disable=too-many-arguments
     68     def __init__(self, ciphersuite=None, signature_algorithm=None, named_group=None,
     69                  cert_sig_alg=None):
     70         self._ciphers = []
     71         self._sig_algs = []
     72         self._named_groups = []
     73         self._cert_sig_algs = []
     74         if ciphersuite:
     75             self.add_ciphersuites(ciphersuite)
     76         if named_group:
     77             self.add_named_groups(named_group)
     78         if signature_algorithm:
     79             self.add_signature_algorithms(signature_algorithm)
     80         if cert_sig_alg:
     81             self.add_cert_signature_algorithms(cert_sig_alg)
     82 
     83     # add_ciphersuites should not override by sub class
     84     def add_ciphersuites(self, *ciphersuites):
     85         self._ciphers.extend(
     86             [cipher for cipher in ciphersuites if cipher not in self._ciphers])
     87 
     88     # add_signature_algorithms should not override by sub class
     89     def add_signature_algorithms(self, *signature_algorithms):
     90         self._sig_algs.extend(
     91             [sig_alg for sig_alg in signature_algorithms if sig_alg not in self._sig_algs])
     92 
     93     # add_named_groups should not override by sub class
     94     def add_named_groups(self, *named_groups):
     95         self._named_groups.extend(
     96             [named_group for named_group in named_groups if named_group not in self._named_groups])
     97 
     98     # add_cert_signature_algorithms should not override by sub class
     99     def add_cert_signature_algorithms(self, *signature_algorithms):
    100         self._cert_sig_algs.extend(
    101             [sig_alg for sig_alg in signature_algorithms if sig_alg not in self._cert_sig_algs])
    102 
    103     # pylint: disable=no-self-use
    104     def pre_checks(self):
    105         return []
    106 
    107     # pylint: disable=no-self-use
    108     def cmd(self):
    109         if not self._cert_sig_algs:
    110             self._cert_sig_algs = list(CERTIFICATES.keys())
    111         return self.pre_cmd()
    112 
    113     # pylint: disable=no-self-use
    114     def post_checks(self):
    115         return []
    116 
    117     # pylint: disable=no-self-use
    118     def pre_cmd(self):
    119         return ['false']
    120 
    121     # pylint: disable=unused-argument,no-self-use
    122     def hrr_post_checks(self, named_group):
    123         return []
    124 
    125 
    126 class OpenSSLBase(TLSProgram):
    127     """
    128     Generate base test commands for OpenSSL.
    129     """
    130 
    131     NAMED_GROUP = {
    132         'secp256r1': 'P-256',
    133         'secp384r1': 'P-384',
    134         'secp521r1': 'P-521',
    135         'x25519': 'X25519',
    136         'x448': 'X448',
    137         'ffdhe2048': 'ffdhe2048',
    138     }
    139 
    140     def cmd(self):
    141         ret = super().cmd()
    142 
    143         if self._ciphers:
    144             ciphersuites = ':'.join(self._ciphers)
    145             ret += ["-ciphersuites {ciphersuites}".format(ciphersuites=ciphersuites)]
    146 
    147         if self._sig_algs:
    148             signature_algorithms = set(self._sig_algs + self._cert_sig_algs)
    149             signature_algorithms = ':'.join(signature_algorithms)
    150             ret += ["-sigalgs {signature_algorithms}".format(
    151                 signature_algorithms=signature_algorithms)]
    152 
    153         if self._named_groups:
    154             named_groups = ':'.join(
    155                 map(lambda named_group: self.NAMED_GROUP[named_group], self._named_groups))
    156             ret += ["-groups {named_groups}".format(named_groups=named_groups)]
    157 
    158         ret += ['-msg -tls1_3']
    159 
    160         return ret
    161 
    162     def pre_checks(self):
    163         ret = ["requires_openssl_tls1_3"]
    164 
    165         # ffdh groups require at least openssl 3.0
    166         ffdh_groups = ['ffdhe2048']
    167 
    168         if any(x in ffdh_groups for x in self._named_groups):
    169             ret = ["requires_openssl_tls1_3_with_ffdh"]
    170 
    171         return ret
    172 
    173 
    174 class OpenSSLServ(OpenSSLBase):
    175     """
    176     Generate test commands for OpenSSL server.
    177     """
    178 
    179     def cmd(self):
    180         ret = super().cmd()
    181         ret += ['-num_tickets 0 -no_resume_ephemeral -no_cache']
    182         return ret
    183 
    184     def post_checks(self):
    185         return ['-c "HTTP/1.0 200 ok"']
    186 
    187     def pre_cmd(self):
    188         ret = ['$O_NEXT_SRV_NO_CERT']
    189         for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs):
    190             ret += ['-cert {cert} -key {key}'.format(cert=cert, key=key)]
    191         return ret
    192 
    193 
    194 class OpenSSLCli(OpenSSLBase):
    195     """
    196     Generate test commands for OpenSSL client.
    197     """
    198 
    199     def pre_cmd(self):
    200         return ['$O_NEXT_CLI_NO_CERT',
    201                 '-CAfile {cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
    202 
    203 
    204 class GnuTLSBase(TLSProgram):
    205     """
    206     Generate base test commands for GnuTLS.
    207     """
    208 
    209     CIPHER_SUITE = {
    210         'TLS_AES_256_GCM_SHA384': [
    211             'AES-256-GCM',
    212             'SHA384',
    213             'AEAD'],
    214         'TLS_AES_128_GCM_SHA256': [
    215             'AES-128-GCM',
    216             'SHA256',
    217             'AEAD'],
    218         'TLS_CHACHA20_POLY1305_SHA256': [
    219             'CHACHA20-POLY1305',
    220             'SHA256',
    221             'AEAD'],
    222         'TLS_AES_128_CCM_SHA256': [
    223             'AES-128-CCM',
    224             'SHA256',
    225             'AEAD'],
    226         'TLS_AES_128_CCM_8_SHA256': [
    227             'AES-128-CCM-8',
    228             'SHA256',
    229             'AEAD']}
    230 
    231     SIGNATURE_ALGORITHM = {
    232         'ecdsa_secp256r1_sha256': ['SIGN-ECDSA-SECP256R1-SHA256'],
    233         'ecdsa_secp521r1_sha512': ['SIGN-ECDSA-SECP521R1-SHA512'],
    234         'ecdsa_secp384r1_sha384': ['SIGN-ECDSA-SECP384R1-SHA384'],
    235         'rsa_pss_rsae_sha256': ['SIGN-RSA-PSS-RSAE-SHA256']}
    236 
    237     NAMED_GROUP = {
    238         'secp256r1': ['GROUP-SECP256R1'],
    239         'secp384r1': ['GROUP-SECP384R1'],
    240         'secp521r1': ['GROUP-SECP521R1'],
    241         'x25519': ['GROUP-X25519'],
    242         'x448': ['GROUP-X448'],
    243         'ffdhe2048': ['GROUP-FFDHE2048'],
    244     }
    245 
    246     def pre_checks(self):
    247         return ["requires_gnutls_tls1_3",
    248                 "requires_gnutls_next_no_ticket"]
    249 
    250     def cmd(self):
    251         ret = super().cmd()
    252 
    253         priority_string_list = []
    254 
    255         def update_priority_string_list(items, map_table):
    256             for item in items:
    257                 for i in map_table[item]:
    258                     if i not in priority_string_list:
    259                         yield i
    260 
    261         if self._ciphers:
    262             priority_string_list.extend(update_priority_string_list(
    263                 self._ciphers, self.CIPHER_SUITE))
    264         else:
    265             priority_string_list.extend(['CIPHER-ALL', 'MAC-ALL'])
    266 
    267         if self._sig_algs:
    268             signature_algorithms = set(self._sig_algs + self._cert_sig_algs)
    269             priority_string_list.extend(update_priority_string_list(
    270                 signature_algorithms, self.SIGNATURE_ALGORITHM))
    271         else:
    272             priority_string_list.append('SIGN-ALL')
    273 
    274 
    275         if self._named_groups:
    276             priority_string_list.extend(update_priority_string_list(
    277                 self._named_groups, self.NAMED_GROUP))
    278         else:
    279             priority_string_list.append('GROUP-ALL')
    280 
    281         priority_string_list = ['NONE'] + \
    282             priority_string_list + ['VERS-TLS1.3']
    283 
    284         priority_string = ':+'.join(priority_string_list)
    285         priority_string += ':%NO_TICKETS'
    286 
    287         ret += ['--priority={priority_string}'.format(
    288             priority_string=priority_string)]
    289         return ret
    290 
    291 class GnuTLSServ(GnuTLSBase):
    292     """
    293     Generate test commands for GnuTLS server.
    294     """
    295 
    296     def pre_cmd(self):
    297         ret = ['$G_NEXT_SRV_NO_CERT', '--http', '--disable-client-cert', '--debug=4']
    298 
    299         for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs):
    300             ret += ['--x509certfile {cert} --x509keyfile {key}'.format(
    301                 cert=cert, key=key)]
    302         return ret
    303 
    304     def post_checks(self):
    305         return ['-c "HTTP/1.0 200 OK"']
    306 
    307 
    308 class GnuTLSCli(GnuTLSBase):
    309     """
    310     Generate test commands for GnuTLS client.
    311     """
    312 
    313     def pre_cmd(self):
    314         return ['$G_NEXT_CLI_NO_CERT', '--debug=4', '--single-key-share',
    315                 '--x509cafile {cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
    316 
    317 
    318 class MbedTLSBase(TLSProgram):
    319     """
    320     Generate base test commands for mbedTLS.
    321     """
    322 
    323     CIPHER_SUITE = {
    324         'TLS_AES_256_GCM_SHA384': 'TLS1-3-AES-256-GCM-SHA384',
    325         'TLS_AES_128_GCM_SHA256': 'TLS1-3-AES-128-GCM-SHA256',
    326         'TLS_CHACHA20_POLY1305_SHA256': 'TLS1-3-CHACHA20-POLY1305-SHA256',
    327         'TLS_AES_128_CCM_SHA256': 'TLS1-3-AES-128-CCM-SHA256',
    328         'TLS_AES_128_CCM_8_SHA256': 'TLS1-3-AES-128-CCM-8-SHA256'}
    329 
    330     def cmd(self):
    331         ret = super().cmd()
    332         ret += ['debug_level=4']
    333 
    334 
    335         if self._ciphers:
    336             ciphers = ','.join(
    337                 map(lambda cipher: self.CIPHER_SUITE[cipher], self._ciphers))
    338             ret += ["force_ciphersuite={ciphers}".format(ciphers=ciphers)]
    339 
    340         if self._sig_algs + self._cert_sig_algs:
    341             ret += ['sig_algs={sig_algs}'.format(
    342                 sig_algs=','.join(set(self._sig_algs + self._cert_sig_algs)))]
    343 
    344         if self._named_groups:
    345             named_groups = ','.join(self._named_groups)
    346             ret += ["groups={named_groups}".format(named_groups=named_groups)]
    347         return ret
    348 
    349     #pylint: disable=missing-function-docstring
    350     def add_ffdh_group_requirements(self, requirement_list):
    351         if 'ffdhe2048' in self._named_groups:
    352             requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
    353         if 'ffdhe3072' in self._named_groups:
    354             requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
    355         if 'ffdhe4096' in self._named_groups:
    356             requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
    357         if 'ffdhe6144' in self._named_groups:
    358             requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
    359         if 'ffdhe8192' in self._named_groups:
    360             requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
    361 
    362     def pre_checks(self):
    363         ret = ['requires_config_enabled MBEDTLS_DEBUG_C',
    364                'requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED']
    365 
    366         if 'rsa_pss_rsae_sha256' in self._sig_algs + self._cert_sig_algs:
    367             ret.append(
    368                 'requires_config_enabled MBEDTLS_X509_RSASSA_PSS_SUPPORT')
    369 
    370         ec_groups = ['secp256r1', 'secp384r1', 'secp521r1', 'x25519', 'x448']
    371         ffdh_groups = ['ffdhe2048', 'ffdhe3072', 'ffdhe4096', 'ffdhe6144', 'ffdhe8192']
    372 
    373         if any(x in ec_groups for x in self._named_groups):
    374             ret.append('requires_config_enabled PSA_WANT_ALG_ECDH')
    375 
    376         if any(x in ffdh_groups for x in self._named_groups):
    377             ret.append('requires_config_enabled PSA_WANT_ALG_FFDH')
    378             self.add_ffdh_group_requirements(ret)
    379 
    380         return ret
    381 
    382 
    383 class MbedTLSServ(MbedTLSBase):
    384     """
    385     Generate test commands for mbedTLS server.
    386     """
    387 
    388     def cmd(self):
    389         ret = super().cmd()
    390         ret += ['tls13_kex_modes=ephemeral cookies=0 tickets=0']
    391         return ret
    392 
    393     def pre_checks(self):
    394         return ['requires_config_enabled MBEDTLS_SSL_SRV_C'] + super().pre_checks()
    395 
    396     def post_checks(self):
    397         check_strings = ["Protocol is TLSv1.3"]
    398         if self._ciphers:
    399             check_strings.append(
    400                 "server hello, chosen ciphersuite: {} ( id={:04d} )".format(
    401                     self.CIPHER_SUITE[self._ciphers[0]],
    402                     CIPHER_SUITE_IANA_VALUE[self._ciphers[0]]))
    403         if self._sig_algs:
    404             check_strings.append(
    405                 "received signature algorithm: 0x{:x}".format(
    406                     SIG_ALG_IANA_VALUE[self._sig_algs[0]]))
    407 
    408         for named_group in self._named_groups:
    409             check_strings += ['got named group: {named_group}({iana_value:04x})'.format(
    410                                 named_group=named_group,
    411                                 iana_value=NAMED_GROUP_IANA_VALUE[named_group])]
    412 
    413         check_strings.append("Certificate verification was skipped")
    414         return ['-s "{}"'.format(i) for i in check_strings]
    415 
    416     def pre_cmd(self):
    417         ret = ['$P_SRV']
    418         for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs):
    419             ret += ['crt_file={cert} key_file={key}'.format(cert=cert, key=key)]
    420         return ret
    421 
    422     def hrr_post_checks(self, named_group):
    423         return ['-s "HRR selected_group: {:s}"'.format(named_group)]
    424 
    425 
    426 class MbedTLSCli(MbedTLSBase):
    427     """
    428     Generate test commands for mbedTLS client.
    429     """
    430 
    431     def pre_cmd(self):
    432         return ['$P_CLI',
    433                 'ca_file={cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
    434 
    435     def pre_checks(self):
    436         return ['requires_config_enabled MBEDTLS_SSL_CLI_C'] + super().pre_checks()
    437 
    438     def hrr_post_checks(self, named_group):
    439         ret = ['-c "received HelloRetryRequest message"']
    440         ret += ['-c "selected_group ( {:d} )"'.format(NAMED_GROUP_IANA_VALUE[named_group])]
    441         return ret
    442 
    443     def post_checks(self):
    444         check_strings = ["Protocol is TLSv1.3"]
    445         if self._ciphers:
    446             check_strings.append(
    447                 "server hello, chosen ciphersuite: ( {:04x} ) - {}".format(
    448                     CIPHER_SUITE_IANA_VALUE[self._ciphers[0]],
    449                     self.CIPHER_SUITE[self._ciphers[0]]))
    450         if self._sig_algs:
    451             check_strings.append(
    452                 "Certificate Verify: Signature algorithm ( {:04x} )".format(
    453                     SIG_ALG_IANA_VALUE[self._sig_algs[0]]))
    454 
    455         for named_group in self._named_groups:
    456             check_strings += ['NamedGroup: {named_group} ( {iana_value:x} )'.format(
    457                                 named_group=named_group,
    458                                 iana_value=NAMED_GROUP_IANA_VALUE[named_group])]
    459 
    460         check_strings.append("Verifying peer X.509 certificate... ok")
    461         return ['-c "{}"'.format(i) for i in check_strings]
    462 
    463 
    464 SERVER_CLASSES = {'OpenSSL': OpenSSLServ, 'GnuTLS': GnuTLSServ, 'mbedTLS': MbedTLSServ}
    465 CLIENT_CLASSES = {'OpenSSL': OpenSSLCli, 'GnuTLS': GnuTLSCli, 'mbedTLS': MbedTLSCli}
    466 
    467 
    468 def generate_compat_test(client=None, server=None, cipher=None, named_group=None, sig_alg=None):
    469     """
    470     Generate test case with `ssl-opt.sh` format.
    471     """
    472     name = 'TLS 1.3 {client[0]}->{server[0]}: {cipher},{named_group},{sig_alg}'.format(
    473         client=client, server=server, cipher=cipher[4:], sig_alg=sig_alg, named_group=named_group)
    474 
    475     server_object = SERVER_CLASSES[server](ciphersuite=cipher,
    476                                            named_group=named_group,
    477                                            signature_algorithm=sig_alg,
    478                                            cert_sig_alg=sig_alg)
    479     client_object = CLIENT_CLASSES[client](ciphersuite=cipher,
    480                                            named_group=named_group,
    481                                            signature_algorithm=sig_alg,
    482                                            cert_sig_alg=sig_alg)
    483 
    484     cmd = ['run_test "{}"'.format(name),
    485            '"{}"'.format(' '.join(server_object.cmd())),
    486            '"{}"'.format(' '.join(client_object.cmd())),
    487            '0']
    488     cmd += server_object.post_checks()
    489     cmd += client_object.post_checks()
    490     cmd += ['-C "received HelloRetryRequest message"']
    491     prefix = ' \\\n' + (' '*9)
    492     cmd = prefix.join(cmd)
    493     return '\n'.join(server_object.pre_checks() + client_object.pre_checks() + [cmd])
    494 
    495 
    496 def generate_hrr_compat_test(client=None, server=None,
    497                              client_named_group=None, server_named_group=None,
    498                              cert_sig_alg=None):
    499     """
    500     Generate Hello Retry Request test case with `ssl-opt.sh` format.
    501     """
    502     name = 'TLS 1.3 {client[0]}->{server[0]}: HRR {c_named_group} -> {s_named_group}'.format(
    503         client=client, server=server, c_named_group=client_named_group,
    504         s_named_group=server_named_group)
    505     server_object = SERVER_CLASSES[server](named_group=server_named_group,
    506                                            cert_sig_alg=cert_sig_alg)
    507 
    508     client_object = CLIENT_CLASSES[client](named_group=client_named_group,
    509                                            cert_sig_alg=cert_sig_alg)
    510     client_object.add_named_groups(server_named_group)
    511 
    512     cmd = ['run_test "{}"'.format(name),
    513            '"{}"'.format(' '.join(server_object.cmd())),
    514            '"{}"'.format(' '.join(client_object.cmd())),
    515            '0']
    516     cmd += server_object.post_checks()
    517     cmd += client_object.post_checks()
    518     cmd += server_object.hrr_post_checks(server_named_group)
    519     cmd += client_object.hrr_post_checks(server_named_group)
    520     prefix = ' \\\n' + (' '*9)
    521     cmd = prefix.join(cmd)
    522     return '\n'.join(server_object.pre_checks() +
    523                      client_object.pre_checks() +
    524                      [cmd])
    525 
    526 SSL_OUTPUT_HEADER = '''\
    527 # TLS 1.3 interoperability test cases (equivalent of compat.sh for TLS 1.3).
    528 #
    529 # Automatically generated by {cmd}. Do not edit!
    530 
    531 # Copyright The Mbed TLS Contributors
    532 # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
    533 '''
    534 DATA_FILES_PATH_VAR = '''
    535 DATA_FILES_PATH=../framework/data_files
    536 '''
    537 
    538 def main():
    539     """
    540     Main function of this program
    541     """
    542     parser = argparse.ArgumentParser()
    543 
    544     parser.add_argument('-o', '--output',
    545                         default='tests/opt-testcases/tls13-compat.sh',
    546                         help='Output file path (not used with -1)')
    547 
    548     parser.add_argument('-1', '--single', action='store_true',
    549                         help='Print a single test case')
    550     # Single mode used to be the default.
    551     parser.add_argument('-a', '--generate-all-tls13-compat-tests',
    552                         action='store_false', dest='single',
    553                         help='Generate all test cases (negates -1) (default)')
    554 
    555     parser.add_argument('--list-ciphers', action='store_true',
    556                         default=False, help='List supported ciphersuites')
    557 
    558     parser.add_argument('--list-sig-algs', action='store_true',
    559                         default=False, help='List supported signature algorithms')
    560 
    561     parser.add_argument('--list-named-groups', action='store_true',
    562                         default=False, help='List supported named groups')
    563 
    564     parser.add_argument('--list-servers', action='store_true',
    565                         default=False, help='List supported TLS servers')
    566 
    567     parser.add_argument('--list-clients', action='store_true',
    568                         default=False, help='List supported TLS Clients')
    569 
    570     parser.add_argument('server', choices=SERVER_CLASSES.keys(), nargs='?',
    571                         default=list(SERVER_CLASSES.keys())[0],
    572                         help='Choose TLS server program for test')
    573     parser.add_argument('client', choices=CLIENT_CLASSES.keys(), nargs='?',
    574                         default=list(CLIENT_CLASSES.keys())[0],
    575                         help='Choose TLS client program for test')
    576     parser.add_argument('cipher', choices=CIPHER_SUITE_IANA_VALUE.keys(), nargs='?',
    577                         default=list(CIPHER_SUITE_IANA_VALUE.keys())[0],
    578                         help='Choose cipher suite for test')
    579     parser.add_argument('sig_alg', choices=SIG_ALG_IANA_VALUE.keys(), nargs='?',
    580                         default=list(SIG_ALG_IANA_VALUE.keys())[0],
    581                         help='Choose cipher suite for test')
    582     parser.add_argument('named_group', choices=NAMED_GROUP_IANA_VALUE.keys(), nargs='?',
    583                         default=list(NAMED_GROUP_IANA_VALUE.keys())[0],
    584                         help='Choose cipher suite for test')
    585 
    586     args = parser.parse_args()
    587 
    588     def get_all_test_cases():
    589         # Generate normal compat test cases
    590         for client, server, cipher, named_group, sig_alg in \
    591             itertools.product(CLIENT_CLASSES.keys(),
    592                               SERVER_CLASSES.keys(),
    593                               CIPHER_SUITE_IANA_VALUE.keys(),
    594                               NAMED_GROUP_IANA_VALUE.keys(),
    595                               SIG_ALG_IANA_VALUE.keys()):
    596             if server == 'mbedTLS' or client == 'mbedTLS':
    597                 yield generate_compat_test(client=client, server=server,
    598                                            cipher=cipher, named_group=named_group,
    599                                            sig_alg=sig_alg)
    600 
    601 
    602         # Generate Hello Retry Request  compat test cases
    603         for client, server, client_named_group, server_named_group in \
    604             itertools.product(CLIENT_CLASSES.keys(),
    605                               SERVER_CLASSES.keys(),
    606                               NAMED_GROUP_IANA_VALUE.keys(),
    607                               NAMED_GROUP_IANA_VALUE.keys()):
    608 
    609             if (client == 'mbedTLS' or server == 'mbedTLS') and \
    610                 client_named_group != server_named_group:
    611                 yield generate_hrr_compat_test(client=client, server=server,
    612                                                client_named_group=client_named_group,
    613                                                server_named_group=server_named_group,
    614                                                cert_sig_alg="ecdsa_secp256r1_sha256")
    615 
    616     if not args.single:
    617         if args.output:
    618             with open(args.output, 'w', encoding="utf-8") as f:
    619                 f.write(SSL_OUTPUT_HEADER.format(
    620                     filename=os.path.basename(args.output),
    621                     cmd=os.path.basename(sys.argv[0])))
    622                 f.write(DATA_FILES_PATH_VAR)
    623                 f.write('\n\n'.join(get_all_test_cases()))
    624                 f.write('\n')
    625         else:
    626             print('\n\n'.join(get_all_test_cases()))
    627         return 0
    628 
    629     if args.list_ciphers or args.list_sig_algs or args.list_named_groups \
    630             or args.list_servers or args.list_clients:
    631         if args.list_ciphers:
    632             print(*CIPHER_SUITE_IANA_VALUE.keys())
    633         if args.list_sig_algs:
    634             print(*SIG_ALG_IANA_VALUE.keys())
    635         if args.list_named_groups:
    636             print(*NAMED_GROUP_IANA_VALUE.keys())
    637         if args.list_servers:
    638             print(*SERVER_CLASSES.keys())
    639         if args.list_clients:
    640             print(*CLIENT_CLASSES.keys())
    641         return 0
    642 
    643     print(generate_compat_test(server=args.server, client=args.client, sig_alg=args.sig_alg,
    644                                cipher=args.cipher, named_group=args.named_group))
    645     return 0
    646 
    647 
    648 if __name__ == "__main__":
    649     sys.exit(main())