From d5d5b60c077fc0a2c14d9936d2cde564d930113a Mon Sep 17 00:00:00 2001 From: XiaokangQian Date: Mon, 23 May 2022 09:16:20 +0000 Subject: [PATCH] Add comprehensive test cases for TLS1.3 server side Change-Id: I544cb12b3ffe5edd7d59fa54342ca7db5b5c8a2a Signed-off-by: XiaokangQian --- programs/ssl/ssl_server2.c | 20 ++ tests/scripts/generate_tls13_compat_tests.py | 302 +++++++++++++++++++ tests/ssl-opt.sh | 4 + 3 files changed, 326 insertions(+) diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index c144b0563a..4251817522 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -2382,6 +2382,22 @@ int main( int argc, char *argv[] ) { sig_alg_list[i++] = MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512; } + else if( strcmp( q, "rsa_pss_rsae_sha256" ) == 0 ) + { + sig_alg_list[i++] = MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256; + } + else if( strcmp( q, "rsa_pss_rsae_sha384" ) == 0 ) + { + sig_alg_list[i++] = MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384; + } + else if( strcmp( q, "rsa_pss_rsae_sha512" ) == 0 ) + { + sig_alg_list[i++] = MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512; + } + else if( strcmp( q, "rsa_pkcs1_sha256" ) == 0 ) + { + sig_alg_list[i++] = MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256; + } else { mbedtls_printf( "unknown signature algorithm %s\n", q ); @@ -2389,6 +2405,10 @@ int main( int argc, char *argv[] ) mbedtls_printf( "ecdsa_secp256r1_sha256 " ); mbedtls_printf( "ecdsa_secp384r1_sha384 " ); mbedtls_printf( "ecdsa_secp521r1_sha512 " ); + mbedtls_printf( "rsa_pss_rsae_sha256 " ); + mbedtls_printf( "rsa_pss_rsae_sha384 " ); + mbedtls_printf( "rsa_pss_rsae_sha512 " ); + mbedtls_printf( "rsa_pkcs1_sha256 " ); mbedtls_printf( "\n" ); goto exit; } diff --git a/tests/scripts/generate_tls13_compat_tests.py b/tests/scripts/generate_tls13_compat_tests.py index d67c222dff..3b139b3584 100755 --- a/tests/scripts/generate_tls13_compat_tests.py +++ b/tests/scripts/generate_tls13_compat_tests.py @@ -275,6 +275,232 @@ class GnuTLSServ(TLSProgram): return ret +class MbedTLSServ(TLSProgram): + """ + Generate test commands for mbedTLS server. + """ + + CIPHER_SUITE = { + 'TLS_AES_256_GCM_SHA384': 'TLS1-3-AES-256-GCM-SHA384', + 'TLS_AES_128_GCM_SHA256': 'TLS1-3-AES-128-GCM-SHA256', + 'TLS_CHACHA20_POLY1305_SHA256': 'TLS1-3-CHACHA20-POLY1305-SHA256', + 'TLS_AES_128_CCM_SHA256': 'TLS1-3-AES-128-CCM-SHA256', + 'TLS_AES_128_CCM_8_SHA256': 'TLS1-3-AES-128-CCM-8-SHA256'} + + def cmd(self): + super().cmd() + ret = ['$P_SRV_NO_CERT'] + ret += ['server_addr=127.0.0.1', 'server_port=$SRV_PORT', + 'debug_level=4', 'force_version=tls13'] + for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs): + ret += ['crt_file={cert} key_file={key}'.format(cert=cert, key=key)] + ret += ['ca_file={cafile}'.format( + cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)] + + if self._ciphers: + ciphers = ','.join( + map(lambda cipher: self.CIPHER_SUITE[cipher], self._ciphers)) + ret += ["force_ciphersuite={ciphers}".format(ciphers=ciphers)] + + if self._sig_algs + self._cert_sig_algs: + ret += ['sig_algs={sig_algs}'.format( + sig_algs=','.join(set(self._sig_algs + self._cert_sig_algs)))] + + if self._named_groups: + named_groups = ','.join(self._named_groups) + ret += ["curves={named_groups}".format(named_groups=named_groups)] + + ret += ['tls13_kex_modes=ephemeral cookies=0 tickets=0'] + ret = ' '.join(ret) + return ret + + def pre_checks(self): + ret = ['requires_config_enabled MBEDTLS_DEBUG_C', + 'requires_config_enabled MBEDTLS_SSL_CLI_C', + 'requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3'] + + if self._compat_mode: + ret += ['requires_config_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE'] + + if 'rsa_pss_rsae_sha256' in self._sig_algs + self._cert_sig_algs: + ret.append( + 'requires_config_enabled MBEDTLS_X509_RSASSA_PSS_SUPPORT') + return ret + + def post_checks(self): + check_strings = ["Protocol is TLSv1.3"] + if self._ciphers: + check_strings.append( + "server hello, chosen ciphersuite: {} ( id={:04d} )".format( + self.CIPHER_SUITE[self._ciphers[0]], + CIPHER_SUITE_IANA_VALUE[self._ciphers[0]])) + if self._sig_algs: + check_strings.append( + "received signature algorithm: 0x{:x}".format( + SIG_ALG_IANA_VALUE[self._sig_algs[0]])) + + for named_group in self._named_groups: + check_strings += ['got named group: {named_group}({iana_value:04x})'.format( + named_group=named_group, + iana_value=NAMED_GROUP_IANA_VALUE[named_group])] + + check_strings.append("Verifying peer X.509 certificate... ok") + return ['-s "{}"'.format(i) for i in check_strings] + + +class OpenSSLCli(TLSProgram): + """ + Generate test commands for OpenSSL client. + """ + + NAMED_GROUP = { + 'secp256r1': 'P-256', + 'secp384r1': 'P-384', + 'secp521r1': 'P-521', + 'x25519': 'X25519', + 'x448': 'X448', + } + + def cmd(self): + super().cmd() + ret = ['$O_NEXT_CLI_NO_CERT'] + for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs): + ret += ['-cert {cert} -key {key}'.format(cert=cert, key=key)] + + ret += ['-CAfile {cafile}'.format( + cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)] + + if self._ciphers: + ciphersuites = ':'.join(self._ciphers) + ret += ["-ciphersuites {ciphersuites}".format(ciphersuites=ciphersuites)] + + if self._sig_algs: + signature_algorithms = set(self._sig_algs + self._cert_sig_algs) + signature_algorithms = ':'.join(signature_algorithms) + ret += ["-sigalgs {signature_algorithms}".format( + signature_algorithms=signature_algorithms)] + + if self._named_groups: + named_groups = ':'.join( + map(lambda named_group: self.NAMED_GROUP[named_group], self._named_groups)) + ret += ["-groups {named_groups}".format(named_groups=named_groups)] + + ret += ['-msg -tls1_3'] + if not self._compat_mode: + ret += ['-no_middlebox'] + + return ' '.join(ret) + + def pre_checks(self): + return ["requires_openssl_tls1_3"] + + def post_checks(self): + return ['-s "HTTP/1.0 200 OK"'] + + +class GnuTLSCli(TLSProgram): + """ + Generate test commands for GnuTLS client. + """ + + CIPHER_SUITE = { + 'TLS_AES_256_GCM_SHA384': [ + 'AES-256-GCM', + 'SHA384', + 'AEAD'], + 'TLS_AES_128_GCM_SHA256': [ + 'AES-128-GCM', + 'SHA256', + 'AEAD'], + 'TLS_CHACHA20_POLY1305_SHA256': [ + 'CHACHA20-POLY1305', + 'SHA256', + 'AEAD'], + 'TLS_AES_128_CCM_SHA256': [ + 'AES-128-CCM', + 'SHA256', + 'AEAD'], + 'TLS_AES_128_CCM_8_SHA256': [ + 'AES-128-CCM-8', + 'SHA256', + 'AEAD']} + + SIGNATURE_ALGORITHM = { + 'ecdsa_secp256r1_sha256': ['SIGN-ECDSA-SECP256R1-SHA256'], + 'ecdsa_secp521r1_sha512': ['SIGN-ECDSA-SECP521R1-SHA512'], + 'ecdsa_secp384r1_sha384': ['SIGN-ECDSA-SECP384R1-SHA384'], + 'rsa_pss_rsae_sha256': ['SIGN-RSA-PSS-RSAE-SHA256']} + + NAMED_GROUP = { + 'secp256r1': ['GROUP-SECP256R1'], + 'secp384r1': ['GROUP-SECP384R1'], + 'secp521r1': ['GROUP-SECP521R1'], + 'x25519': ['GROUP-X25519'], + 'x448': ['GROUP-X448'], + } + + def pre_checks(self): + return ["requires_gnutls_tls1_3", + "requires_gnutls_next_no_ticket", + "requires_gnutls_next_disable_tls13_compat", ] + + def post_checks(self): + return ['-c "HTTP/1.0 200 OK"'] + + def cmd(self): + super().cmd() + ret = ['$G_NEXT_CLI_NO_CERT', '--debug=4'] + ret += ['localhost', '-p $SRV_PORT', '--single-key-share'] + ret += ['--x509cafile {cafile}'.format( + cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)] + + for _, cert, key in map(lambda sig_alg: CERTIFICATES[sig_alg], self._cert_sig_algs): + ret += ['--x509certfile {cert} --x509keyfile {key}'.format( + cert=cert, key=key)] + + priority_string_list = [] + + def update_priority_string_list(items, map_table): + for item in items: + for i in map_table[item]: + if i not in priority_string_list: + yield i + + if self._ciphers: + priority_string_list.extend(update_priority_string_list( + self._ciphers, self.CIPHER_SUITE)) + else: + priority_string_list.extend(['CIPHER-ALL', 'MAC-ALL']) + + if self._sig_algs: + signature_algorithms = set(self._sig_algs + self._cert_sig_algs) + priority_string_list.extend(update_priority_string_list( + signature_algorithms, self.SIGNATURE_ALGORITHM)) + else: + priority_string_list.append('SIGN-ALL') + + + if self._named_groups: + priority_string_list.extend(update_priority_string_list( + self._named_groups, self.NAMED_GROUP)) + else: + priority_string_list.append('GROUP-ALL') + + priority_string_list = ['NONE'] + \ + priority_string_list + ['VERS-TLS1.3'] + + priority_string = ':+'.join(priority_string_list) + priority_string += ':%NO_TICKETS' + + if not self._compat_mode: + priority_string += [':%DISABLE_TLS13_COMPAT_MODE'] + + ret += ['--priority={priority_string}'.format( + priority_string=priority_string)] + ret = ' '.join(ret) + return ret + + class MbedTLSCli(TLSProgram): """ Generate test commands for mbedTLS client. @@ -348,6 +574,8 @@ class MbedTLSCli(TLSProgram): SERVER_CLASSES = {'OpenSSL': OpenSSLServ, 'GnuTLS': GnuTLSServ} CLIENT_CLASSES = {'mbedTLS': MbedTLSCli} +SERVER_SERVER_CLASSES = {'mbedTLS': MbedTLSServ} +SERVER_CLIENT_CLASSES = {'OpenSSL': OpenSSLCli, 'GnuTLS': GnuTLSCli, 'mbedTLS': MbedTLSCli} def generate_compat_test(client=None, server=None, cipher=None, named_group=None, sig_alg=None): """ @@ -375,6 +603,33 @@ def generate_compat_test(client=None, server=None, cipher=None, named_group=None return '\n'.join(server_object.pre_checks() + client_object.pre_checks() + [cmd]) +def generate_server_side_compat_test(client=None, server=None, cipher=None, + named_group=None, sig_alg=None): + """ + Generate server side test case with `ssl-opt.sh` format. + """ + name = 'TLS 1.3 server side {client[0]}->{server[0]}: {cipher},{named_group},{sig_alg}'.format( + client=client, server=server, cipher=cipher[4:], sig_alg=sig_alg, named_group=named_group) + + server_object = SERVER_SERVER_CLASSES[server](ciphersuite=cipher, + named_group=named_group, + signature_algorithm=sig_alg, + cert_sig_alg=sig_alg) + client_object = SERVER_CLIENT_CLASSES[client](ciphersuite=cipher, + named_group=named_group, + signature_algorithm=sig_alg, + cert_sig_alg=sig_alg) + + cmd = ['run_test "{}"'.format(name), '"{}"'.format( + server_object.cmd()), '"{}"'.format(client_object.cmd()), '0'] + cmd += server_object.post_checks() + cmd += client_object.post_checks() + cmd += ['-C "received HelloRetryRequest message"'] + prefix = ' \\\n' + (' '*9) + cmd = prefix.join(cmd) + return '\n'.join(server_object.pre_checks() + client_object.pre_checks() + [cmd]) + + def generate_hrr_compat_test(client=None, server=None, client_named_group=None, server_named_group=None, cert_sig_alg=None): @@ -404,6 +659,32 @@ def generate_hrr_compat_test(client=None, server=None, client_object.pre_checks() + [cmd]) +def generate_server_hrr_compat_test(client=None, server=None, + client_named_group=None, server_named_group=None, + cert_sig_alg=None): + """ + Generate server side Hello Retry Request test case with `ssl-opt.sh` format. + """ + name = 'TLS 1.3 {client[0]}->{server[0]}: Server HRR {c_named_group} -> {s_named_group}'.format( + client=client, server=server, c_named_group=client_named_group, + s_named_group=server_named_group) + server_object = SERVER_SERVER_CLASSES[server](named_group=server_named_group, + cert_sig_alg=cert_sig_alg) + + client_object = SERVER_CLIENT_CLASSES[client](named_group=client_named_group, + cert_sig_alg=cert_sig_alg) + client_object.add_named_groups(server_named_group) + + cmd = ['run_test "{}"'.format(name), '"{}"'.format( + server_object.cmd()), '"{}"'.format(client_object.cmd()), '0'] + cmd += server_object.post_checks() + cmd += client_object.post_checks() + cmd += ['-s "HRR selected_group: {:s}"'.format(server_named_group)] + prefix = ' \\\n' + (' '*9) + cmd = prefix.join(cmd) + return '\n'.join(server_object.pre_checks() + + client_object.pre_checks() + + [cmd]) SSL_OUTPUT_HEADER = '''#!/bin/sh @@ -491,6 +772,16 @@ def main(): cipher=cipher, named_group=named_group, sig_alg=sig_alg) + for client, server, cipher, named_group, sig_alg in \ + itertools.product(SERVER_CLIENT_CLASSES.keys(), + SERVER_SERVER_CLASSES.keys(), + CIPHER_SUITE_IANA_VALUE.keys(), + NAMED_GROUP_IANA_VALUE.keys(), + SIG_ALG_IANA_VALUE.keys()): + yield generate_server_side_compat_test(client=client, server=server, + cipher=cipher, named_group=named_group, + sig_alg=sig_alg) + # Generate Hello Retry Request compat test cases for client, server, client_named_group, server_named_group in \ itertools.product(CLIENT_CLASSES.keys(), @@ -503,6 +794,17 @@ def main(): server_named_group=server_named_group, cert_sig_alg="ecdsa_secp256r1_sha256") + for client, server, client_named_group, server_named_group in \ + itertools.product(SERVER_CLIENT_CLASSES.keys(), + SERVER_SERVER_CLASSES.keys(), + NAMED_GROUP_IANA_VALUE.keys(), + NAMED_GROUP_IANA_VALUE.keys()): + if client_named_group != server_named_group: + yield generate_server_hrr_compat_test(client=client, server=server, + client_named_group=client_named_group, + server_named_group=server_named_group, + cert_sig_alg="ecdsa_secp256r1_sha256") + if args.generate_all_tls13_compat_tests: if args.output: with open(args.output, 'w', encoding="utf-8") as f: diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 6eaa53477d..aafe0e00bd 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -82,6 +82,7 @@ if [ -n "${OPENSSL_NEXT:-}" ]; then O_NEXT_SRV="$OPENSSL_NEXT s_server -www -cert data_files/server5.crt -key data_files/server5.key" O_NEXT_SRV_NO_CERT="$OPENSSL_NEXT s_server -www " O_NEXT_CLI="echo 'GET / HTTP/1.0' | $OPENSSL_NEXT s_client -CAfile data_files/test-ca_cat12.crt" + O_NEXT_CLI_NO_CERT="echo 'GET / HTTP/1.0' | $OPENSSL_NEXT s_client" else O_NEXT_SRV=false O_NEXT_SRV_NO_CERT=false @@ -98,6 +99,7 @@ fi if [ -n "${GNUTLS_NEXT_CLI:-}" ]; then G_NEXT_CLI="echo 'GET / HTTP/1.0' | $GNUTLS_NEXT_CLI --x509cafile data_files/test-ca_cat12.crt" + G_NEXT_CLI_NO_CERT="echo 'GET / HTTP/1.0' | $GNUTLS_NEXT_CLI" else G_NEXT_CLI=false fi @@ -1538,6 +1540,7 @@ SRV_DELAY_SECONDS=0 # Note: Using 'localhost' rather than 127.0.0.1 here is unwise, as on many # machines that will resolve to ::1, and we don't want ipv6 here. P_SRV="$P_SRV server_addr=127.0.0.1 server_port=$SRV_PORT" +P_SRV_NO_CERT="$P_SRV server_addr=127.0.0.1 server_port=$SRV_PORT" P_CLI="$P_CLI server_addr=127.0.0.1 server_port=+SRV_PORT" P_PXY="$P_PXY server_addr=127.0.0.1 server_port=$SRV_PORT listen_addr=127.0.0.1 listen_port=$PXY_PORT ${SEED:+"seed=$SEED"}" O_SRV="$O_SRV -accept $SRV_PORT" @@ -1554,6 +1557,7 @@ if [ -n "${OPENSSL_NEXT:-}" ]; then O_NEXT_SRV="$O_NEXT_SRV -accept $SRV_PORT" O_NEXT_SRV_NO_CERT="$O_NEXT_SRV_NO_CERT -accept $SRV_PORT" O_NEXT_CLI="$O_NEXT_CLI -connect 127.0.0.1:+SRV_PORT" + O_NEXT_CLI_NO_CERT="$O_NEXT_CLI_NO_CERT -connect 127.0.0.1:+SRV_PORT" fi if [ -n "${GNUTLS_NEXT_SERV:-}" ]; then