mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-02-04 15:39:53 +00:00
Merge pull request #9673 from eleuzi01/issue-54-fw
Move TLS auxiliary test scripts to the framework
This commit is contained in:
commit
13d2939563
@ -1 +1 @@
|
||||
Subproject commit 1de0641e789d3c38b3ce99d7922002992cbe816c
|
||||
Subproject commit 311d8acd1c329d6417db576ba5219973c500d696
|
@ -61,11 +61,11 @@ if(GEN_FILES)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ssl_debug_helpers_generated.c
|
||||
COMMAND
|
||||
${MBEDTLS_PYTHON_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/generate_ssl_debug_helpers.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../framework/scripts/generate_ssl_debug_helpers.py
|
||||
--mbedtls-root ${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/generate_ssl_debug_helpers.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../framework/scripts/generate_ssl_debug_helpers.py
|
||||
${tls_error_headers}
|
||||
)
|
||||
else()
|
||||
|
@ -364,11 +364,11 @@ $(TF_PSA_CRYPTO_DRIVERS_BUILTIN_SRC_PATH)/error.c:
|
||||
echo " Gen $@"
|
||||
$(PERL) ../scripts/generate_errors.pl
|
||||
|
||||
ssl_debug_helpers_generated.c: $(gen_file_dep) ../scripts/generate_ssl_debug_helpers.py
|
||||
ssl_debug_helpers_generated.c: $(gen_file_dep) ../framework/scripts/generate_ssl_debug_helpers.py
|
||||
ssl_debug_helpers_generated.c: $(gen_file_dep) $(filter-out %config%,$(wildcard ../include/mbedtls/*.h))
|
||||
ssl_debug_helpers_generated.c:
|
||||
echo " Gen $@"
|
||||
$(PYTHON) ../scripts/generate_ssl_debug_helpers.py --mbedtls-root .. .
|
||||
$(PYTHON) ../framework/scripts/generate_ssl_debug_helpers.py --mbedtls-root .. .
|
||||
|
||||
version_features.c: $(gen_file_dep) ../scripts/generate_features.pl
|
||||
version_features.c: $(gen_file_dep) ../scripts/data_files/version_features.fmt
|
||||
|
@ -1,416 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Generate library/ssl_debug_helpers_generated.c
|
||||
|
||||
The code generated by this module includes debug helper functions that can not be
|
||||
implemented by fixed codes.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import textwrap
|
||||
import argparse
|
||||
|
||||
import framework_scripts_path # pylint: disable=unused-import
|
||||
from mbedtls_framework import build_tree
|
||||
|
||||
|
||||
def remove_c_comments(string):
|
||||
"""
|
||||
Remove C style comments from input string
|
||||
"""
|
||||
string_pattern = r"(?P<string>\".*?\"|\'.*?\')"
|
||||
comment_pattern = r"(?P<comment>/\*.*?\*/|//[^\r\n]*$)"
|
||||
pattern = re.compile(string_pattern + r'|' + comment_pattern,
|
||||
re.MULTILINE | re.DOTALL)
|
||||
|
||||
def replacer(match):
|
||||
if match.lastgroup == 'comment':
|
||||
return ""
|
||||
return match.group()
|
||||
return pattern.sub(replacer, string)
|
||||
|
||||
|
||||
class CondDirectiveNotMatch(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def preprocess_c_source_code(source, *classes):
|
||||
"""
|
||||
Simple preprocessor for C source code.
|
||||
|
||||
Only processes condition directives without expanding them.
|
||||
Yield object according to the classes input. Most match firstly
|
||||
|
||||
If the directive pair does not match , raise CondDirectiveNotMatch.
|
||||
|
||||
Assume source code does not include comments and compile pass.
|
||||
|
||||
"""
|
||||
|
||||
pattern = re.compile(r"^[ \t]*#[ \t]*" +
|
||||
r"(?P<directive>(if[ \t]|ifndef[ \t]|ifdef[ \t]|else|endif))" +
|
||||
r"[ \t]*(?P<param>(.*\\\n)*.*$)",
|
||||
re.MULTILINE)
|
||||
stack = []
|
||||
|
||||
def _yield_objects(s, d, p, st, end):
|
||||
"""
|
||||
Output matched source piece
|
||||
"""
|
||||
nonlocal stack
|
||||
start_line, end_line = '', ''
|
||||
if stack:
|
||||
start_line = '#{} {}'.format(d, p)
|
||||
if d == 'if':
|
||||
end_line = '#endif /* {} */'.format(p)
|
||||
elif d == 'ifdef':
|
||||
end_line = '#endif /* defined({}) */'.format(p)
|
||||
else:
|
||||
end_line = '#endif /* !defined({}) */'.format(p)
|
||||
has_instance = False
|
||||
for cls in classes:
|
||||
for instance in cls.extract(s, st, end):
|
||||
if has_instance is False:
|
||||
has_instance = True
|
||||
yield pair_start, start_line
|
||||
yield instance.span()[0], instance
|
||||
if has_instance:
|
||||
yield start, end_line
|
||||
|
||||
for match in pattern.finditer(source):
|
||||
|
||||
directive = match.groupdict()['directive'].strip()
|
||||
param = match.groupdict()['param']
|
||||
start, end = match.span()
|
||||
|
||||
if directive in ('if', 'ifndef', 'ifdef'):
|
||||
stack.append((directive, param, start, end))
|
||||
continue
|
||||
|
||||
if not stack:
|
||||
raise CondDirectiveNotMatch()
|
||||
|
||||
pair_directive, pair_param, pair_start, pair_end = stack.pop()
|
||||
yield from _yield_objects(source,
|
||||
pair_directive,
|
||||
pair_param,
|
||||
pair_end,
|
||||
start)
|
||||
|
||||
if directive == 'endif':
|
||||
continue
|
||||
|
||||
if pair_directive == 'if':
|
||||
directive = 'if'
|
||||
param = "!( {} )".format(pair_param)
|
||||
elif pair_directive == 'ifdef':
|
||||
directive = 'ifndef'
|
||||
param = pair_param
|
||||
else:
|
||||
directive = 'ifdef'
|
||||
param = pair_param
|
||||
|
||||
stack.append((directive, param, start, end))
|
||||
assert not stack, len(stack)
|
||||
|
||||
|
||||
class EnumDefinition:
|
||||
"""
|
||||
Generate helper functions around enumeration.
|
||||
|
||||
Currently, it generate translation function from enum value to string.
|
||||
Enum definition looks like:
|
||||
[typedef] enum [prefix name] { [body] } [suffix name];
|
||||
|
||||
Known limitation:
|
||||
- the '}' and ';' SHOULD NOT exist in different macro blocks. Like
|
||||
```
|
||||
enum test {
|
||||
....
|
||||
#if defined(A)
|
||||
....
|
||||
};
|
||||
#else
|
||||
....
|
||||
};
|
||||
#endif
|
||||
```
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def extract(cls, source_code, start=0, end=-1):
|
||||
enum_pattern = re.compile(r'enum\s*(?P<prefix_name>\w*)\s*' +
|
||||
r'{\s*(?P<body>[^}]*)}' +
|
||||
r'\s*(?P<suffix_name>\w*)\s*;',
|
||||
re.MULTILINE | re.DOTALL)
|
||||
|
||||
for match in enum_pattern.finditer(source_code, start, end):
|
||||
yield EnumDefinition(source_code,
|
||||
span=match.span(),
|
||||
group=match.groupdict())
|
||||
|
||||
def __init__(self, source_code, span=None, group=None):
|
||||
assert isinstance(group, dict)
|
||||
prefix_name = group.get('prefix_name', None)
|
||||
suffix_name = group.get('suffix_name', None)
|
||||
body = group.get('body', None)
|
||||
assert prefix_name or suffix_name
|
||||
assert body
|
||||
assert span
|
||||
# If suffix_name exists, it is a typedef
|
||||
self._prototype = suffix_name if suffix_name else 'enum ' + prefix_name
|
||||
self._name = suffix_name if suffix_name else prefix_name
|
||||
self._body = body
|
||||
self._source = source_code
|
||||
self._span = span
|
||||
|
||||
def __repr__(self):
|
||||
return 'Enum({},{})'.format(self._name, self._span)
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def span(self):
|
||||
return self._span
|
||||
|
||||
def generate_translation_function(self):
|
||||
"""
|
||||
Generate function for translating value to string
|
||||
"""
|
||||
translation_table = []
|
||||
|
||||
for line in self._body.splitlines():
|
||||
|
||||
if line.strip().startswith('#'):
|
||||
# Preprocess directive, keep it in table
|
||||
translation_table.append(line.strip())
|
||||
continue
|
||||
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
for field in line.strip().split(','):
|
||||
if not field.strip():
|
||||
continue
|
||||
member = field.strip().split()[0]
|
||||
translation_table.append(
|
||||
'{space}case {member}:\n{space} return "{member}";'
|
||||
.format(member=member, space=' '*8)
|
||||
)
|
||||
|
||||
body = textwrap.dedent('''\
|
||||
const char *{name}_str( {prototype} in )
|
||||
{{
|
||||
switch (in) {{
|
||||
{translation_table}
|
||||
default:
|
||||
return "UNKNOWN_VALUE";
|
||||
}}
|
||||
}}
|
||||
''')
|
||||
body = body.format(translation_table='\n'.join(translation_table),
|
||||
name=self._name,
|
||||
prototype=self._prototype)
|
||||
return body
|
||||
|
||||
|
||||
class SignatureAlgorithmDefinition:
|
||||
"""
|
||||
Generate helper functions for signature algorithms.
|
||||
|
||||
It generates translation function from signature algorithm define to string.
|
||||
Signature algorithm definition looks like:
|
||||
#define MBEDTLS_TLS1_3_SIG_[ upper case signature algorithm ] [ value(hex) ]
|
||||
|
||||
Known limitation:
|
||||
- the definitions SHOULD exist in same macro blocks.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def extract(cls, source_code, start=0, end=-1):
|
||||
sig_alg_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_TLS1_3_SIG_\w+)\s+' +
|
||||
r'(?P<value>0[xX][0-9a-fA-F]+)$',
|
||||
re.MULTILINE | re.DOTALL)
|
||||
matches = list(sig_alg_pattern.finditer(source_code, start, end))
|
||||
if matches:
|
||||
yield SignatureAlgorithmDefinition(source_code, definitions=matches)
|
||||
|
||||
def __init__(self, source_code, definitions=None):
|
||||
if definitions is None:
|
||||
definitions = []
|
||||
assert isinstance(definitions, list) and definitions
|
||||
self._definitions = definitions
|
||||
self._source = source_code
|
||||
|
||||
def __repr__(self):
|
||||
return 'SigAlgs({})'.format(self._definitions[0].span())
|
||||
|
||||
def span(self):
|
||||
return self._definitions[0].span()
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Generate function for translating value to string
|
||||
"""
|
||||
translation_table = []
|
||||
for m in self._definitions:
|
||||
name = m.groupdict()['name']
|
||||
return_val = name[len('MBEDTLS_TLS1_3_SIG_'):].lower()
|
||||
translation_table.append(
|
||||
' case {}:\n return "{}";'.format(name, return_val))
|
||||
|
||||
body = textwrap.dedent('''\
|
||||
const char *mbedtls_ssl_sig_alg_to_str( uint16_t in )
|
||||
{{
|
||||
switch( in )
|
||||
{{
|
||||
{translation_table}
|
||||
}};
|
||||
|
||||
return "UNKNOWN";
|
||||
}}''')
|
||||
body = body.format(translation_table='\n'.join(translation_table))
|
||||
return body
|
||||
|
||||
|
||||
class NamedGroupDefinition:
|
||||
"""
|
||||
Generate helper functions for named group
|
||||
|
||||
It generates translation function from named group define to string.
|
||||
Named group definition looks like:
|
||||
#define MBEDTLS_SSL_IANA_TLS_GROUP_[ upper case named group ] [ value(hex) ]
|
||||
|
||||
Known limitation:
|
||||
- the definitions SHOULD exist in same macro blocks.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def extract(cls, source_code, start=0, end=-1):
|
||||
named_group_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_SSL_IANA_TLS_GROUP_\w+)\s+' +
|
||||
r'(?P<value>0[xX][0-9a-fA-F]+)$',
|
||||
re.MULTILINE | re.DOTALL)
|
||||
matches = list(named_group_pattern.finditer(source_code, start, end))
|
||||
if matches:
|
||||
yield NamedGroupDefinition(source_code, definitions=matches)
|
||||
|
||||
def __init__(self, source_code, definitions=None):
|
||||
if definitions is None:
|
||||
definitions = []
|
||||
assert isinstance(definitions, list) and definitions
|
||||
self._definitions = definitions
|
||||
self._source = source_code
|
||||
|
||||
def __repr__(self):
|
||||
return 'NamedGroup({})'.format(self._definitions[0].span())
|
||||
|
||||
def span(self):
|
||||
return self._definitions[0].span()
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Generate function for translating value to string
|
||||
"""
|
||||
translation_table = []
|
||||
for m in self._definitions:
|
||||
name = m.groupdict()['name']
|
||||
iana_name = name[len('MBEDTLS_SSL_IANA_TLS_GROUP_'):].lower()
|
||||
translation_table.append(' case {}:\n return "{}";'.format(name, iana_name))
|
||||
|
||||
body = textwrap.dedent('''\
|
||||
const char *mbedtls_ssl_named_group_to_str( uint16_t in )
|
||||
{{
|
||||
switch( in )
|
||||
{{
|
||||
{translation_table}
|
||||
}};
|
||||
|
||||
return "UNKNOWN";
|
||||
}}''')
|
||||
body = body.format(translation_table='\n'.join(translation_table))
|
||||
return body
|
||||
|
||||
|
||||
OUTPUT_C_TEMPLATE = '''\
|
||||
/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
|
||||
|
||||
/**
|
||||
* \\file ssl_debug_helpers_generated.c
|
||||
*
|
||||
* \\brief Automatically generated helper functions for debugging
|
||||
*/
|
||||
/*
|
||||
* Copyright The Mbed TLS Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ssl_misc.h"
|
||||
|
||||
#if defined(MBEDTLS_DEBUG_C)
|
||||
|
||||
#include "ssl_debug_helpers.h"
|
||||
|
||||
{functions}
|
||||
|
||||
#endif /* MBEDTLS_DEBUG_C */
|
||||
/* End of automatically generated file. */
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def generate_ssl_debug_helpers(output_directory, mbedtls_root):
|
||||
"""
|
||||
Generate functions of debug helps
|
||||
"""
|
||||
mbedtls_root = os.path.abspath(
|
||||
mbedtls_root or build_tree.guess_mbedtls_root())
|
||||
with open(os.path.join(mbedtls_root, 'include/mbedtls/ssl.h')) as f:
|
||||
source_code = remove_c_comments(f.read())
|
||||
|
||||
definitions = dict()
|
||||
for start, instance in preprocess_c_source_code(source_code,
|
||||
EnumDefinition,
|
||||
SignatureAlgorithmDefinition,
|
||||
NamedGroupDefinition):
|
||||
if start in definitions:
|
||||
continue
|
||||
if isinstance(instance, EnumDefinition):
|
||||
definition = instance.generate_translation_function()
|
||||
else:
|
||||
definition = instance
|
||||
definitions[start] = definition
|
||||
|
||||
function_definitions = [str(v) for _, v in sorted(definitions.items())]
|
||||
if output_directory == sys.stdout:
|
||||
sys.stdout.write(OUTPUT_C_TEMPLATE.format(
|
||||
functions='\n'.join(function_definitions)))
|
||||
else:
|
||||
with open(os.path.join(output_directory, 'ssl_debug_helpers_generated.c'), 'w') as f:
|
||||
f.write(OUTPUT_C_TEMPLATE.format(
|
||||
functions='\n'.join(function_definitions)))
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Command line entry
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--mbedtls-root', nargs='?', default=None,
|
||||
help='root directory of mbedtls source code')
|
||||
parser.add_argument('output_directory', nargs='?',
|
||||
default='library', help='source/header files location')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
generate_ssl_debug_helpers(args.output_directory, args.mbedtls_root)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -13,7 +13,7 @@ python scripts\generate_driver_wrappers.py || exit /b 1
|
||||
perl scripts\generate_errors.pl || exit /b 1
|
||||
perl scripts\generate_query_config.pl || exit /b 1
|
||||
perl scripts\generate_features.pl || exit /b 1
|
||||
python scripts\generate_ssl_debug_helpers.py || exit /b 1
|
||||
python framework\scripts\generate_ssl_debug_helpers.py || exit /b 1
|
||||
|
||||
@rem @@@@ Build @@@@
|
||||
perl scripts\generate_visualc_files.pl || exit /b 1
|
||||
@ -29,4 +29,4 @@ python framework\scripts\generate_ecp_tests.py --directory tf-psa-crypto\tests\s
|
||||
python framework\scripts\generate_psa_tests.py --directory tf-psa-crypto\tests\suites || exit /b 1
|
||||
python framework\scripts\generate_test_keys.py --output tests\src\test_keys.h || exit /b 1
|
||||
python framework\scripts\generate_test_cert_macros.py --output tests\src\test_certs.h || exit /b 1
|
||||
python tests\scripts\generate_tls13_compat_tests.py || exit /b 1
|
||||
python framework\scripts\generate_tls13_compat_tests.py || exit /b 1
|
||||
|
@ -71,9 +71,9 @@ if(GEN_FILES)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
COMMAND
|
||||
"${MBEDTLS_PYTHON_EXECUTABLE}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_tls13_compat_tests.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../framework/scripts/generate_tls13_compat_tests.py"
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_tls13_compat_tests.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../framework/scripts/generate_tls13_compat_tests.py
|
||||
)
|
||||
add_custom_target(tls13-compat.sh
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/opt-testcases/tls13-compat.sh)
|
||||
|
@ -62,9 +62,9 @@ GENERATED_FILES += src/test_keys.h src/test_certs.h
|
||||
# Generated files needed to (fully) run ssl-opt.sh
|
||||
.PHONY: ssl-opt
|
||||
|
||||
opt-testcases/tls13-compat.sh: scripts/generate_tls13_compat_tests.py
|
||||
opt-testcases/tls13-compat.sh: ../framework/scripts/generate_tls13_compat_tests.py
|
||||
echo " Gen $@"
|
||||
$(PYTHON) scripts/generate_tls13_compat_tests.py -o $@
|
||||
$(PYTHON) ../framework/scripts/generate_tls13_compat_tests.py -o $@
|
||||
GENERATED_FILES += opt-testcases/tls13-compat.sh
|
||||
ssl-opt: opt-testcases/tls13-compat.sh
|
||||
|
||||
|
@ -290,7 +290,7 @@ reset_ciphersuites()
|
||||
# list of entries of the form "STANDARD_NAME=PROGRAM_NAME".
|
||||
translate_ciphers()
|
||||
{
|
||||
ciphers=$(scripts/translate_ciphers.py "$@")
|
||||
ciphers=$(../framework/scripts/translate_ciphers.py "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "translate_ciphers.py failed with exit code $1" >&2
|
||||
echo "$2" >&2
|
||||
|
@ -173,8 +173,8 @@ if in_mbedtls_repo; then
|
||||
check scripts/generate_errors.pl ${builtin_drivers_dir}/error.c
|
||||
check scripts/generate_query_config.pl programs/test/query_config.c
|
||||
check scripts/generate_features.pl library/version_features.c
|
||||
check scripts/generate_ssl_debug_helpers.py library/ssl_debug_helpers_generated.c
|
||||
check tests/scripts/generate_tls13_compat_tests.py tests/opt-testcases/tls13-compat.sh
|
||||
check framework/scripts/generate_ssl_debug_helpers.py library/ssl_debug_helpers_generated.c
|
||||
check framework/scripts/generate_tls13_compat_tests.py tests/opt-testcases/tls13-compat.sh
|
||||
check framework/scripts/generate_test_cert_macros.py tests/src/test_certs.h
|
||||
# generate_visualc_files enumerates source files (library/*.c). It doesn't
|
||||
# care about their content, but the files must exist. So it must run after
|
||||
|
@ -160,6 +160,6 @@ component_check_test_helpers () {
|
||||
./framework/scripts/test_generate_test_code.py 2>&1
|
||||
|
||||
msg "unit test: translate_ciphers.py"
|
||||
python3 -m unittest tests/scripts/translate_ciphers.py 2>&1
|
||||
python3 -m unittest framework/scripts/translate_ciphers.py 2>&1
|
||||
}
|
||||
|
||||
|
@ -1,649 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# generate_tls13_compat_tests.py
|
||||
#
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
Generate TLSv1.3 Compat test cases
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import itertools
|
||||
from collections import namedtuple
|
||||
|
||||
# define certificates configuration entry
|
||||
Certificate = namedtuple("Certificate", ['cafile', 'certfile', 'keyfile'])
|
||||
# define the certificate parameters for signature algorithms
|
||||
CERTIFICATES = {
|
||||
'ecdsa_secp256r1_sha256': Certificate('$DATA_FILES_PATH/test-ca2.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp256r1.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp256r1.key'),
|
||||
'ecdsa_secp384r1_sha384': Certificate('$DATA_FILES_PATH/test-ca2.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp384r1.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp384r1.key'),
|
||||
'ecdsa_secp521r1_sha512': Certificate('$DATA_FILES_PATH/test-ca2.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp521r1.crt',
|
||||
'$DATA_FILES_PATH/ecdsa_secp521r1.key'),
|
||||
'rsa_pss_rsae_sha256': Certificate('$DATA_FILES_PATH/test-ca_cat12.crt',
|
||||
'$DATA_FILES_PATH/server2-sha256.crt',
|
||||
'$DATA_FILES_PATH/server2.key')
|
||||
}
|
||||
|
||||
CIPHER_SUITE_IANA_VALUE = {
|
||||
"TLS_AES_128_GCM_SHA256": 0x1301,
|
||||
"TLS_AES_256_GCM_SHA384": 0x1302,
|
||||
"TLS_CHACHA20_POLY1305_SHA256": 0x1303,
|
||||
"TLS_AES_128_CCM_SHA256": 0x1304,
|
||||
"TLS_AES_128_CCM_8_SHA256": 0x1305
|
||||
}
|
||||
|
||||
SIG_ALG_IANA_VALUE = {
|
||||
"ecdsa_secp256r1_sha256": 0x0403,
|
||||
"ecdsa_secp384r1_sha384": 0x0503,
|
||||
"ecdsa_secp521r1_sha512": 0x0603,
|
||||
'rsa_pss_rsae_sha256': 0x0804,
|
||||
}
|
||||
|
||||
NAMED_GROUP_IANA_VALUE = {
|
||||
'secp256r1': 0x17,
|
||||
'secp384r1': 0x18,
|
||||
'secp521r1': 0x19,
|
||||
'x25519': 0x1d,
|
||||
'x448': 0x1e,
|
||||
# Only one finite field group to keep testing time within reasonable bounds.
|
||||
'ffdhe2048': 0x100,
|
||||
}
|
||||
|
||||
class TLSProgram:
|
||||
"""
|
||||
Base class for generate server/client command.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, ciphersuite=None, signature_algorithm=None, named_group=None,
|
||||
cert_sig_alg=None):
|
||||
self._ciphers = []
|
||||
self._sig_algs = []
|
||||
self._named_groups = []
|
||||
self._cert_sig_algs = []
|
||||
if ciphersuite:
|
||||
self.add_ciphersuites(ciphersuite)
|
||||
if named_group:
|
||||
self.add_named_groups(named_group)
|
||||
if signature_algorithm:
|
||||
self.add_signature_algorithms(signature_algorithm)
|
||||
if cert_sig_alg:
|
||||
self.add_cert_signature_algorithms(cert_sig_alg)
|
||||
|
||||
# add_ciphersuites should not override by sub class
|
||||
def add_ciphersuites(self, *ciphersuites):
|
||||
self._ciphers.extend(
|
||||
[cipher for cipher in ciphersuites if cipher not in self._ciphers])
|
||||
|
||||
# add_signature_algorithms should not override by sub class
|
||||
def add_signature_algorithms(self, *signature_algorithms):
|
||||
self._sig_algs.extend(
|
||||
[sig_alg for sig_alg in signature_algorithms if sig_alg not in self._sig_algs])
|
||||
|
||||
# add_named_groups should not override by sub class
|
||||
def add_named_groups(self, *named_groups):
|
||||
self._named_groups.extend(
|
||||
[named_group for named_group in named_groups if named_group not in self._named_groups])
|
||||
|
||||
# add_cert_signature_algorithms should not override by sub class
|
||||
def add_cert_signature_algorithms(self, *signature_algorithms):
|
||||
self._cert_sig_algs.extend(
|
||||
[sig_alg for sig_alg in signature_algorithms if sig_alg not in self._cert_sig_algs])
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def pre_checks(self):
|
||||
return []
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def cmd(self):
|
||||
if not self._cert_sig_algs:
|
||||
self._cert_sig_algs = list(CERTIFICATES.keys())
|
||||
return self.pre_cmd()
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def post_checks(self):
|
||||
return []
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def pre_cmd(self):
|
||||
return ['false']
|
||||
|
||||
# pylint: disable=unused-argument,no-self-use
|
||||
def hrr_post_checks(self, named_group):
|
||||
return []
|
||||
|
||||
|
||||
class OpenSSLBase(TLSProgram):
|
||||
"""
|
||||
Generate base test commands for OpenSSL.
|
||||
"""
|
||||
|
||||
NAMED_GROUP = {
|
||||
'secp256r1': 'P-256',
|
||||
'secp384r1': 'P-384',
|
||||
'secp521r1': 'P-521',
|
||||
'x25519': 'X25519',
|
||||
'x448': 'X448',
|
||||
'ffdhe2048': 'ffdhe2048',
|
||||
}
|
||||
|
||||
def cmd(self):
|
||||
ret = super().cmd()
|
||||
|
||||
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']
|
||||
|
||||
return ret
|
||||
|
||||
def pre_checks(self):
|
||||
ret = ["requires_openssl_tls1_3"]
|
||||
|
||||
# ffdh groups require at least openssl 3.0
|
||||
ffdh_groups = ['ffdhe2048']
|
||||
|
||||
if any(x in ffdh_groups for x in self._named_groups):
|
||||
ret = ["requires_openssl_tls1_3_with_ffdh"]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class OpenSSLServ(OpenSSLBase):
|
||||
"""
|
||||
Generate test commands for OpenSSL server.
|
||||
"""
|
||||
|
||||
def cmd(self):
|
||||
ret = super().cmd()
|
||||
ret += ['-num_tickets 0 -no_resume_ephemeral -no_cache']
|
||||
return ret
|
||||
|
||||
def post_checks(self):
|
||||
return ['-c "HTTP/1.0 200 ok"']
|
||||
|
||||
def pre_cmd(self):
|
||||
ret = ['$O_NEXT_SRV_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)]
|
||||
return ret
|
||||
|
||||
|
||||
class OpenSSLCli(OpenSSLBase):
|
||||
"""
|
||||
Generate test commands for OpenSSL client.
|
||||
"""
|
||||
|
||||
def pre_cmd(self):
|
||||
return ['$O_NEXT_CLI_NO_CERT',
|
||||
'-CAfile {cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
|
||||
|
||||
|
||||
class GnuTLSBase(TLSProgram):
|
||||
"""
|
||||
Generate base test commands for GnuTLS.
|
||||
"""
|
||||
|
||||
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'],
|
||||
'ffdhe2048': ['GROUP-FFDHE2048'],
|
||||
}
|
||||
|
||||
def pre_checks(self):
|
||||
return ["requires_gnutls_tls1_3",
|
||||
"requires_gnutls_next_no_ticket"]
|
||||
|
||||
def cmd(self):
|
||||
ret = super().cmd()
|
||||
|
||||
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'
|
||||
|
||||
ret += ['--priority={priority_string}'.format(
|
||||
priority_string=priority_string)]
|
||||
return ret
|
||||
|
||||
class GnuTLSServ(GnuTLSBase):
|
||||
"""
|
||||
Generate test commands for GnuTLS server.
|
||||
"""
|
||||
|
||||
def pre_cmd(self):
|
||||
ret = ['$G_NEXT_SRV_NO_CERT', '--http', '--disable-client-cert', '--debug=4']
|
||||
|
||||
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)]
|
||||
return ret
|
||||
|
||||
def post_checks(self):
|
||||
return ['-c "HTTP/1.0 200 OK"']
|
||||
|
||||
|
||||
class GnuTLSCli(GnuTLSBase):
|
||||
"""
|
||||
Generate test commands for GnuTLS client.
|
||||
"""
|
||||
|
||||
def pre_cmd(self):
|
||||
return ['$G_NEXT_CLI_NO_CERT', '--debug=4', '--single-key-share',
|
||||
'--x509cafile {cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
|
||||
|
||||
|
||||
class MbedTLSBase(TLSProgram):
|
||||
"""
|
||||
Generate base test commands for mbedTLS.
|
||||
"""
|
||||
|
||||
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):
|
||||
ret = super().cmd()
|
||||
ret += ['debug_level=4']
|
||||
|
||||
|
||||
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 += ["groups={named_groups}".format(named_groups=named_groups)]
|
||||
return ret
|
||||
|
||||
#pylint: disable=missing-function-docstring
|
||||
def add_ffdh_group_requirements(self, requirement_list):
|
||||
if 'ffdhe2048' in self._named_groups:
|
||||
requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
|
||||
if 'ffdhe3072' in self._named_groups:
|
||||
requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
|
||||
if 'ffdhe4096' in self._named_groups:
|
||||
requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
|
||||
if 'ffdhe6144' in self._named_groups:
|
||||
requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
|
||||
if 'ffdhe8192' in self._named_groups:
|
||||
requirement_list.append('requires_config_enabled PSA_WANT_DH_RFC7919_2048')
|
||||
|
||||
def pre_checks(self):
|
||||
ret = ['requires_config_enabled MBEDTLS_DEBUG_C',
|
||||
'requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED']
|
||||
|
||||
if 'rsa_pss_rsae_sha256' in self._sig_algs + self._cert_sig_algs:
|
||||
ret.append(
|
||||
'requires_config_enabled MBEDTLS_X509_RSASSA_PSS_SUPPORT')
|
||||
|
||||
ec_groups = ['secp256r1', 'secp384r1', 'secp521r1', 'x25519', 'x448']
|
||||
ffdh_groups = ['ffdhe2048', 'ffdhe3072', 'ffdhe4096', 'ffdhe6144', 'ffdhe8192']
|
||||
|
||||
if any(x in ec_groups for x in self._named_groups):
|
||||
ret.append('requires_config_enabled PSA_WANT_ALG_ECDH')
|
||||
|
||||
if any(x in ffdh_groups for x in self._named_groups):
|
||||
ret.append('requires_config_enabled PSA_WANT_ALG_FFDH')
|
||||
self.add_ffdh_group_requirements(ret)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class MbedTLSServ(MbedTLSBase):
|
||||
"""
|
||||
Generate test commands for mbedTLS server.
|
||||
"""
|
||||
|
||||
def cmd(self):
|
||||
ret = super().cmd()
|
||||
ret += ['tls13_kex_modes=ephemeral cookies=0 tickets=0']
|
||||
return ret
|
||||
|
||||
def pre_checks(self):
|
||||
return ['requires_config_enabled MBEDTLS_SSL_SRV_C'] + super().pre_checks()
|
||||
|
||||
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("Certificate verification was skipped")
|
||||
return ['-s "{}"'.format(i) for i in check_strings]
|
||||
|
||||
def pre_cmd(self):
|
||||
ret = ['$P_SRV']
|
||||
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)]
|
||||
return ret
|
||||
|
||||
def hrr_post_checks(self, named_group):
|
||||
return ['-s "HRR selected_group: {:s}"'.format(named_group)]
|
||||
|
||||
|
||||
class MbedTLSCli(MbedTLSBase):
|
||||
"""
|
||||
Generate test commands for mbedTLS client.
|
||||
"""
|
||||
|
||||
def pre_cmd(self):
|
||||
return ['$P_CLI',
|
||||
'ca_file={cafile}'.format(cafile=CERTIFICATES[self._cert_sig_algs[0]].cafile)]
|
||||
|
||||
def pre_checks(self):
|
||||
return ['requires_config_enabled MBEDTLS_SSL_CLI_C'] + super().pre_checks()
|
||||
|
||||
def hrr_post_checks(self, named_group):
|
||||
ret = ['-c "received HelloRetryRequest message"']
|
||||
ret += ['-c "selected_group ( {:d} )"'.format(NAMED_GROUP_IANA_VALUE[named_group])]
|
||||
return ret
|
||||
|
||||
def post_checks(self):
|
||||
check_strings = ["Protocol is TLSv1.3"]
|
||||
if self._ciphers:
|
||||
check_strings.append(
|
||||
"server hello, chosen ciphersuite: ( {:04x} ) - {}".format(
|
||||
CIPHER_SUITE_IANA_VALUE[self._ciphers[0]],
|
||||
self.CIPHER_SUITE[self._ciphers[0]]))
|
||||
if self._sig_algs:
|
||||
check_strings.append(
|
||||
"Certificate Verify: Signature algorithm ( {:04x} )".format(
|
||||
SIG_ALG_IANA_VALUE[self._sig_algs[0]]))
|
||||
|
||||
for named_group in self._named_groups:
|
||||
check_strings += ['NamedGroup: {named_group} ( {iana_value:x} )'.format(
|
||||
named_group=named_group,
|
||||
iana_value=NAMED_GROUP_IANA_VALUE[named_group])]
|
||||
|
||||
check_strings.append("Verifying peer X.509 certificate... ok")
|
||||
return ['-c "{}"'.format(i) for i in check_strings]
|
||||
|
||||
|
||||
SERVER_CLASSES = {'OpenSSL': OpenSSLServ, 'GnuTLS': GnuTLSServ, 'mbedTLS': MbedTLSServ}
|
||||
CLIENT_CLASSES = {'OpenSSL': OpenSSLCli, 'GnuTLS': GnuTLSCli, 'mbedTLS': MbedTLSCli}
|
||||
|
||||
|
||||
def generate_compat_test(client=None, server=None, cipher=None, named_group=None, sig_alg=None):
|
||||
"""
|
||||
Generate test case with `ssl-opt.sh` format.
|
||||
"""
|
||||
name = 'TLS 1.3 {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_CLASSES[server](ciphersuite=cipher,
|
||||
named_group=named_group,
|
||||
signature_algorithm=sig_alg,
|
||||
cert_sig_alg=sig_alg)
|
||||
client_object = CLIENT_CLASSES[client](ciphersuite=cipher,
|
||||
named_group=named_group,
|
||||
signature_algorithm=sig_alg,
|
||||
cert_sig_alg=sig_alg)
|
||||
|
||||
cmd = ['run_test "{}"'.format(name),
|
||||
'"{}"'.format(' '.join(server_object.cmd())),
|
||||
'"{}"'.format(' '.join(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):
|
||||
"""
|
||||
Generate Hello Retry Request test case with `ssl-opt.sh` format.
|
||||
"""
|
||||
name = 'TLS 1.3 {client[0]}->{server[0]}: 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_CLASSES[server](named_group=server_named_group,
|
||||
cert_sig_alg=cert_sig_alg)
|
||||
|
||||
client_object = 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(' '.join(server_object.cmd())),
|
||||
'"{}"'.format(' '.join(client_object.cmd())),
|
||||
'0']
|
||||
cmd += server_object.post_checks()
|
||||
cmd += client_object.post_checks()
|
||||
cmd += server_object.hrr_post_checks(server_named_group)
|
||||
cmd += client_object.hrr_post_checks(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 = '''\
|
||||
# TLS 1.3 interoperability test cases (equivalent of compat.sh for TLS 1.3).
|
||||
#
|
||||
# Automatically generated by {cmd}. Do not edit!
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
'''
|
||||
DATA_FILES_PATH_VAR = '''
|
||||
DATA_FILES_PATH=../framework/data_files
|
||||
'''
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function of this program
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-o', '--output',
|
||||
default='tests/opt-testcases/tls13-compat.sh',
|
||||
help='Output file path (not used with -1)')
|
||||
|
||||
parser.add_argument('-1', '--single', action='store_true',
|
||||
help='Print a single test case')
|
||||
# Single mode used to be the default.
|
||||
parser.add_argument('-a', '--generate-all-tls13-compat-tests',
|
||||
action='store_false', dest='single',
|
||||
help='Generate all test cases (negates -1) (default)')
|
||||
|
||||
parser.add_argument('--list-ciphers', action='store_true',
|
||||
default=False, help='List supported ciphersuites')
|
||||
|
||||
parser.add_argument('--list-sig-algs', action='store_true',
|
||||
default=False, help='List supported signature algorithms')
|
||||
|
||||
parser.add_argument('--list-named-groups', action='store_true',
|
||||
default=False, help='List supported named groups')
|
||||
|
||||
parser.add_argument('--list-servers', action='store_true',
|
||||
default=False, help='List supported TLS servers')
|
||||
|
||||
parser.add_argument('--list-clients', action='store_true',
|
||||
default=False, help='List supported TLS Clients')
|
||||
|
||||
parser.add_argument('server', choices=SERVER_CLASSES.keys(), nargs='?',
|
||||
default=list(SERVER_CLASSES.keys())[0],
|
||||
help='Choose TLS server program for test')
|
||||
parser.add_argument('client', choices=CLIENT_CLASSES.keys(), nargs='?',
|
||||
default=list(CLIENT_CLASSES.keys())[0],
|
||||
help='Choose TLS client program for test')
|
||||
parser.add_argument('cipher', choices=CIPHER_SUITE_IANA_VALUE.keys(), nargs='?',
|
||||
default=list(CIPHER_SUITE_IANA_VALUE.keys())[0],
|
||||
help='Choose cipher suite for test')
|
||||
parser.add_argument('sig_alg', choices=SIG_ALG_IANA_VALUE.keys(), nargs='?',
|
||||
default=list(SIG_ALG_IANA_VALUE.keys())[0],
|
||||
help='Choose cipher suite for test')
|
||||
parser.add_argument('named_group', choices=NAMED_GROUP_IANA_VALUE.keys(), nargs='?',
|
||||
default=list(NAMED_GROUP_IANA_VALUE.keys())[0],
|
||||
help='Choose cipher suite for test')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
def get_all_test_cases():
|
||||
# Generate normal compat test cases
|
||||
for client, server, cipher, named_group, sig_alg in \
|
||||
itertools.product(CLIENT_CLASSES.keys(),
|
||||
SERVER_CLASSES.keys(),
|
||||
CIPHER_SUITE_IANA_VALUE.keys(),
|
||||
NAMED_GROUP_IANA_VALUE.keys(),
|
||||
SIG_ALG_IANA_VALUE.keys()):
|
||||
if server == 'mbedTLS' or client == 'mbedTLS':
|
||||
yield generate_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(),
|
||||
SERVER_CLASSES.keys(),
|
||||
NAMED_GROUP_IANA_VALUE.keys(),
|
||||
NAMED_GROUP_IANA_VALUE.keys()):
|
||||
|
||||
if (client == 'mbedTLS' or server == 'mbedTLS') and \
|
||||
client_named_group != server_named_group:
|
||||
yield generate_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 not args.single:
|
||||
if args.output:
|
||||
with open(args.output, 'w', encoding="utf-8") as f:
|
||||
f.write(SSL_OUTPUT_HEADER.format(
|
||||
filename=os.path.basename(args.output),
|
||||
cmd=os.path.basename(sys.argv[0])))
|
||||
f.write(DATA_FILES_PATH_VAR)
|
||||
f.write('\n\n'.join(get_all_test_cases()))
|
||||
f.write('\n')
|
||||
else:
|
||||
print('\n\n'.join(get_all_test_cases()))
|
||||
return 0
|
||||
|
||||
if args.list_ciphers or args.list_sig_algs or args.list_named_groups \
|
||||
or args.list_servers or args.list_clients:
|
||||
if args.list_ciphers:
|
||||
print(*CIPHER_SUITE_IANA_VALUE.keys())
|
||||
if args.list_sig_algs:
|
||||
print(*SIG_ALG_IANA_VALUE.keys())
|
||||
if args.list_named_groups:
|
||||
print(*NAMED_GROUP_IANA_VALUE.keys())
|
||||
if args.list_servers:
|
||||
print(*SERVER_CLASSES.keys())
|
||||
if args.list_clients:
|
||||
print(*CLIENT_CLASSES.keys())
|
||||
return 0
|
||||
|
||||
print(generate_compat_test(server=args.server, client=args.client, sig_alg=args.sig_alg,
|
||||
cipher=args.cipher, named_group=args.named_group))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,180 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# translate_ciphers.py
|
||||
#
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
Translate standard ciphersuite names to GnuTLS, OpenSSL and Mbed TLS standards.
|
||||
|
||||
To test the translation functions run:
|
||||
python3 -m unittest translate_cipher.py
|
||||
"""
|
||||
|
||||
import re
|
||||
import argparse
|
||||
import unittest
|
||||
|
||||
class TestTranslateCiphers(unittest.TestCase):
|
||||
"""
|
||||
Ensure translate_ciphers.py translates and formats ciphersuite names
|
||||
correctly
|
||||
"""
|
||||
def test_translate_all_cipher_names(self):
|
||||
"""
|
||||
Translate standard ciphersuite names to GnuTLS, OpenSSL and
|
||||
Mbed TLS counterpart. Use only a small subset of ciphers
|
||||
that exercise each step of the translation functions
|
||||
"""
|
||||
ciphers = [
|
||||
("TLS_ECDHE_ECDSA_WITH_NULL_SHA",
|
||||
"+ECDHE-ECDSA:+NULL:+SHA1",
|
||||
"ECDHE-ECDSA-NULL-SHA",
|
||||
"TLS-ECDHE-ECDSA-WITH-NULL-SHA"),
|
||||
("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"+ECDHE-ECDSA:+AES-128-GCM:+AEAD",
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
||||
"TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"),
|
||||
("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"+DHE-RSA:+3DES-CBC:+SHA1",
|
||||
"EDH-RSA-DES-CBC3-SHA",
|
||||
"TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA"),
|
||||
("TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"+RSA:+AES-256-CBC:+SHA1",
|
||||
"AES256-SHA",
|
||||
"TLS-RSA-WITH-AES-256-CBC-SHA"),
|
||||
("TLS_PSK_WITH_3DES_EDE_CBC_SHA",
|
||||
"+PSK:+3DES-CBC:+SHA1",
|
||||
"PSK-3DES-EDE-CBC-SHA",
|
||||
"TLS-PSK-WITH-3DES-EDE-CBC-SHA"),
|
||||
("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
None,
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305",
|
||||
"TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"),
|
||||
("TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
|
||||
"+ECDHE-ECDSA:+AES-128-CCM:+AEAD",
|
||||
None,
|
||||
"TLS-ECDHE-ECDSA-WITH-AES-128-CCM"),
|
||||
("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",
|
||||
None,
|
||||
"ECDHE-ARIA256-GCM-SHA384",
|
||||
"TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384"),
|
||||
]
|
||||
|
||||
for s, g_exp, o_exp, m_exp in ciphers:
|
||||
|
||||
if g_exp is not None:
|
||||
g = translate_gnutls(s)
|
||||
self.assertEqual(g, g_exp)
|
||||
|
||||
if o_exp is not None:
|
||||
o = translate_ossl(s)
|
||||
self.assertEqual(o, o_exp)
|
||||
|
||||
if m_exp is not None:
|
||||
m = translate_mbedtls(s)
|
||||
self.assertEqual(m, m_exp)
|
||||
|
||||
def translate_gnutls(s_cipher):
|
||||
"""
|
||||
Translate s_cipher from standard ciphersuite naming convention
|
||||
and return the GnuTLS naming convention
|
||||
"""
|
||||
|
||||
# Replace "_" with "-" to handle ciphersuite names based on Mbed TLS
|
||||
# naming convention
|
||||
s_cipher = s_cipher.replace("_", "-")
|
||||
|
||||
s_cipher = re.sub(r'\ATLS-', '+', s_cipher)
|
||||
s_cipher = s_cipher.replace("-WITH-", ":+")
|
||||
s_cipher = s_cipher.replace("-EDE", "")
|
||||
|
||||
# SHA in Mbed TLS == SHA1 GnuTLS,
|
||||
# if the last 3 chars are SHA append 1
|
||||
if s_cipher[-3:] == "SHA":
|
||||
s_cipher = s_cipher+"1"
|
||||
|
||||
# CCM or CCM-8 should be followed by ":+AEAD"
|
||||
# Replace "GCM:+SHAxyz" with "GCM:+AEAD"
|
||||
if "CCM" in s_cipher or "GCM" in s_cipher:
|
||||
s_cipher = re.sub(r"GCM-SHA\d\d\d", "GCM", s_cipher)
|
||||
s_cipher = s_cipher+":+AEAD"
|
||||
|
||||
# Replace the last "-" with ":+"
|
||||
else:
|
||||
index = s_cipher.rindex("-")
|
||||
s_cipher = s_cipher[:index] + ":+" + s_cipher[index+1:]
|
||||
|
||||
return s_cipher
|
||||
|
||||
def translate_ossl(s_cipher):
|
||||
"""
|
||||
Translate s_cipher from standard ciphersuite naming convention
|
||||
and return the OpenSSL naming convention
|
||||
"""
|
||||
|
||||
# Replace "_" with "-" to handle ciphersuite names based on Mbed TLS
|
||||
# naming convention
|
||||
s_cipher = s_cipher.replace("_", "-")
|
||||
|
||||
s_cipher = re.sub(r'^TLS-', '', s_cipher)
|
||||
s_cipher = s_cipher.replace("-WITH", "")
|
||||
|
||||
# Remove the "-" from "ABC-xyz"
|
||||
s_cipher = s_cipher.replace("AES-", "AES")
|
||||
s_cipher = s_cipher.replace("CAMELLIA-", "CAMELLIA")
|
||||
s_cipher = s_cipher.replace("ARIA-", "ARIA")
|
||||
|
||||
# Remove "RSA" if it is at the beginning
|
||||
s_cipher = re.sub(r'^RSA-', r'', s_cipher)
|
||||
|
||||
# For all circumstances outside of PSK
|
||||
if "PSK" not in s_cipher:
|
||||
s_cipher = s_cipher.replace("-EDE", "")
|
||||
s_cipher = s_cipher.replace("3DES-CBC", "DES-CBC3")
|
||||
|
||||
# Remove "CBC" if it is not prefixed by DES
|
||||
s_cipher = re.sub(r'(?<!DES-)CBC-', r'', s_cipher)
|
||||
|
||||
# ECDHE-RSA-ARIA does not exist in OpenSSL
|
||||
s_cipher = s_cipher.replace("ECDHE-RSA-ARIA", "ECDHE-ARIA")
|
||||
|
||||
# POLY1305 should not be followed by anything
|
||||
if "POLY1305" in s_cipher:
|
||||
index = s_cipher.rindex("POLY1305")
|
||||
s_cipher = s_cipher[:index+8]
|
||||
|
||||
# If DES is being used, Replace DHE with EDH
|
||||
if "DES" in s_cipher and "DHE" in s_cipher and "ECDHE" not in s_cipher:
|
||||
s_cipher = s_cipher.replace("DHE", "EDH")
|
||||
|
||||
return s_cipher
|
||||
|
||||
def translate_mbedtls(s_cipher):
|
||||
"""
|
||||
Translate s_cipher from standard ciphersuite naming convention
|
||||
and return Mbed TLS ciphersuite naming convention
|
||||
"""
|
||||
|
||||
# Replace "_" with "-"
|
||||
s_cipher = s_cipher.replace("_", "-")
|
||||
|
||||
return s_cipher
|
||||
|
||||
def format_ciphersuite_names(mode, names):
|
||||
t = {"g": translate_gnutls,
|
||||
"o": translate_ossl,
|
||||
"m": translate_mbedtls
|
||||
}[mode]
|
||||
return " ".join(c + '=' + t(c) for c in names)
|
||||
|
||||
def main(target, names):
|
||||
print(format_ciphersuite_names(target, names))
|
||||
|
||||
if __name__ == "__main__":
|
||||
PARSER = argparse.ArgumentParser()
|
||||
PARSER.add_argument('target', metavar='TARGET', choices=['o', 'g', 'm'])
|
||||
PARSER.add_argument('names', metavar='NAMES', nargs='+')
|
||||
ARGS = PARSER.parse_args()
|
||||
main(ARGS.target, ARGS.names)
|
Loading…
x
Reference in New Issue
Block a user