From 23318bde24e4558a9f69486c8229866363546322 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 3 Mar 2026 13:28:46 +0100 Subject: [PATCH 1/6] Remove PSA status coverage log test We haven't used this in years. It's obsolete because this functionality should now be provided by the more general PSA function wrappers (`PSALoggingWrapper` generator), although that work is unfinished. It belongs in TF-PSA-Crypto anyway. So remove it, it's one less little amount of baggage. Signed-off-by: Gilles Peskine --- programs/Makefile | 3 - tests/Makefile | 16 --- .../components-configuration-crypto.sh | 9 -- tests/scripts/psa_collect_statuses.py | 130 ------------------ 4 files changed, 158 deletions(-) delete mode 100755 tests/scripts/psa_collect_statuses.py diff --git a/programs/Makefile b/programs/Makefile index 846361d788..21a69766c1 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -8,9 +8,6 @@ else DLOPEN_LDFLAGS ?= endif -ifdef RECORD_PSA_STATUS_COVERAGE_LOG -LOCAL_CFLAGS += -Werror -DRECORD_PSA_STATUS_COVERAGE_LOG -endif DEP=${MBEDLIBS} ${MBEDTLS_TEST_OBJS} # Only build the dlopen test in shared library builds, and not when building diff --git a/tests/Makefile b/tests/Makefile index 62a7b82e61..06bef9734c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,10 +19,6 @@ LOCAL_CFLAGS += $(TF_PSA_CRYPTO_LIBRARY_PRIVATE_INCLUDE) # on non-POSIX platforms. LOCAL_CFLAGS += -D_POSIX_C_SOURCE=200809L -ifdef RECORD_PSA_STATUS_COVERAGE_LOG -LOCAL_CFLAGS += -Werror -DRECORD_PSA_STATUS_COVERAGE_LOG -endif - GENERATED_MBEDTLS_CONFIG_DATA_FILES := $(patsubst tests/%,%,$(shell \ $(PYTHON) ../framework/scripts/generate_config_tests.py --list || \ echo FAILED \ @@ -110,12 +106,6 @@ include/test/test_keys.h: ../framework/scripts/generate_test_keys.py $(PYTHON) ../framework/scripts/generate_test_keys.py --output $@ TEST_OBJS_DEPS = $(wildcard include/test/*.h include/test/*/*.h) -ifdef RECORD_PSA_STATUS_COVERAGE_LOG -# Explicitly depend on this header because on a clean copy of the source tree, -# it doesn't exist yet and must be generated as part of the build, and -# therefore the wildcard enumeration above doesn't include it. -TEST_OBJS_DEPS += ../framework/tests/include/test/instrument_record_status.h -endif TEST_OBJS_DEPS += include/test/test_certs.h include/test/test_keys.h \ ../tf-psa-crypto/tests/include/test/test_keys.h @@ -311,9 +301,3 @@ libtestdriver1.a: fi $(MAKE) -C ./libtestdriver1/library CFLAGS="-I../../ $(CFLAGS)" LDFLAGS="$(LDFLAGS)" libmbedcrypto.a cp ./libtestdriver1/library/libmbedcrypto.a ../library/libtestdriver1.a - -ifdef RECORD_PSA_STATUS_COVERAGE_LOG -../framework/tests/include/test/instrument_record_status.h: ../tf-psa-crypto/include/psa/crypto.h Makefile - echo " Gen $@" - sed <../tf-psa-crypto/include/psa/crypto.h >$@ -n 's/^psa_status_t \([A-Za-z0-9_]*\)(.*/#define \1(...) RECORD_STATUS("\1", \1(__VA_ARGS__))/p' -endif diff --git a/tests/scripts/components-configuration-crypto.sh b/tests/scripts/components-configuration-crypto.sh index 6a6b0a70de..2227287358 100644 --- a/tests/scripts/components-configuration-crypto.sh +++ b/tests/scripts/components-configuration-crypto.sh @@ -493,15 +493,6 @@ component_test_everest_curve25519_only () { ctest } -component_test_psa_collect_statuses () { - msg "build+test: psa_collect_statuses" # ~30s - scripts/config.py full - tests/scripts/psa_collect_statuses.py - # Check that psa_crypto_init() succeeded at least once - grep -q '^0:psa_crypto_init:' tests/statuses.log - rm -f tests/statuses.log -} - # Check that the specified libraries exist and are empty. are_empty_libraries () { nm "$@" >/dev/null 2>/dev/null diff --git a/tests/scripts/psa_collect_statuses.py b/tests/scripts/psa_collect_statuses.py deleted file mode 100755 index a91e3a3b30..0000000000 --- a/tests/scripts/psa_collect_statuses.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 -"""Describe the test coverage of PSA functions in terms of return statuses. - -1. Build Mbed TLS with -DRECORD_PSA_STATUS_COVERAGE_LOG -2. Run psa_collect_statuses.py - -The output is a series of line of the form "psa_foo PSA_ERROR_XXX". Each -function/status combination appears only once. - -This script must be run from the top of an Mbed TLS source tree. -The build command is "make -DRECORD_PSA_STATUS_COVERAGE_LOG", which is -only supported with make (as opposed to CMake or other build methods). -""" - -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -import argparse -import os -import subprocess -import sys - -DEFAULT_STATUS_LOG_FILE = 'tests/statuses.log' -DEFAULT_PSA_CONSTANT_NAMES = 'tf-psa-crypto/programs/psa/psa_constant_names' - -class Statuses: - """Information about observed return statues of API functions.""" - - def __init__(self): - self.functions = {} - self.codes = set() - self.status_names = {} - - def collect_log(self, log_file_name): - """Read logs from RECORD_PSA_STATUS_COVERAGE_LOG. - - Read logs produced by running Mbed TLS test suites built with - -DRECORD_PSA_STATUS_COVERAGE_LOG. - """ - with open(log_file_name) as log: - for line in log: - value, function, tail = line.split(':', 2) - if function not in self.functions: - self.functions[function] = {} - fdata = self.functions[function] - if value not in self.functions[function]: - fdata[value] = [] - fdata[value].append(tail) - self.codes.add(int(value)) - - def get_constant_names(self, psa_constant_names): - """Run psa_constant_names to obtain names for observed numerical values.""" - values = [str(value) for value in self.codes] - cmd = [psa_constant_names, 'status'] + values - output = subprocess.check_output(cmd).decode('ascii') - for value, name in zip(values, output.rstrip().split('\n')): - self.status_names[value] = name - - def report(self): - """Report observed return values for each function. - - The report is a series of line of the form "psa_foo PSA_ERROR_XXX". - """ - for function in sorted(self.functions.keys()): - fdata = self.functions[function] - names = [self.status_names[value] for value in fdata.keys()] - for name in sorted(names): - sys.stdout.write('{} {}\n'.format(function, name)) - -def collect_status_logs(options): - """Build and run unit tests and report observed function return statuses. - - Build Mbed TLS with -DRECORD_PSA_STATUS_COVERAGE_LOG, run the - test suites and display information about observed return statuses. - """ - rebuilt = False - if not options.use_existing_log and os.path.exists(options.log_file): - os.remove(options.log_file) - if not os.path.exists(options.log_file): - if options.clean_before: - subprocess.check_call(['make', '-f', 'scripts/legacy.make', 'clean'], - cwd='tests', - stdout=sys.stderr) - with open(os.devnull, 'w') as devnull: - make_q_ret = subprocess.call(['make', '-f', 'scripts/legacy.make', - '-q', 'lib', 'tests'], - stdout=devnull, stderr=devnull) - if make_q_ret != 0: - subprocess.check_call(['make', '-f', 'scripts/legacy.make', - 'RECORD_PSA_STATUS_COVERAGE_LOG=1'], - stdout=sys.stderr) - rebuilt = True - subprocess.check_call(['make', '-f', 'scripts/legacy.make', 'test'], - stdout=sys.stderr) - data = Statuses() - data.collect_log(options.log_file) - data.get_constant_names(options.psa_constant_names) - if rebuilt and options.clean_after: - subprocess.check_call(['make', '-f', 'scripts/legacy.make', 'clean'], - cwd='tests', - stdout=sys.stderr) - return data - -def main(): - parser = argparse.ArgumentParser(description=globals()['__doc__']) - parser.add_argument('--clean-after', - action='store_true', - help='Run "make clean" after rebuilding') - parser.add_argument('--clean-before', - action='store_true', - help='Run "make clean" before regenerating the log file)') - parser.add_argument('--log-file', metavar='FILE', - default=DEFAULT_STATUS_LOG_FILE, - help='Log file location (default: {})'.format( - DEFAULT_STATUS_LOG_FILE - )) - parser.add_argument('--psa-constant-names', metavar='PROGRAM', - default=DEFAULT_PSA_CONSTANT_NAMES, - help='Path to psa_constant_names (default: {})'.format( - DEFAULT_PSA_CONSTANT_NAMES - )) - parser.add_argument('--use-existing-log', '-e', - action='store_true', - help='Don\'t regenerate the log file if it exists') - options = parser.parse_args() - data = collect_status_logs(options) - data.report() - -if __name__ == '__main__': - main() From 326fb18585c789f43fd33e9f36d6484f87b7c7fe Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 3 Mar 2026 13:36:38 +0100 Subject: [PATCH 2/6] Move some scripts from mbedtls into the framework Move a bunch of files from `scripts` and `mbedtls/scripts` to the framework. Most are not called from any scripts invoked by the CI, but a couple are. A subsequent commit will adapt the scripts. None of these scripts are referenced from other repositories except in documentation. The following files will be removed, and added to `mbedtls-framework`: * `scripts/ecp_comb_table.py` * `scripts/massif_max.pl` * `tests/scripts/audit-validity-dates.py` (moved to `scripts/`) * `tests/scripts/gen_ctr_drbg.pl` (moved to `scripts/`) * `tests/scripts/gen_gcm_decrypt.pl` (moved to `scripts/`) * `tests/scripts/gen_gcm_encrypt.pl` (moved to `scripts/`) * `tests/scripts/gen_pkcs1_v21_sign_verify.pl` (moved to `scripts/`) * `tests/scripts/generate-afl-tests.sh` (moved to `scripts/`) * `tests/scripts/generate_server9_bad_saltlen.py` (moved to `scripts/`) * `tests/scripts/run-metatests.sh` (moved to `scripts/`) * `tests/scripts/run_demos.py` (moved to `scripts/`) * `tests/scripts/test_config_script.py` (moved to `scripts/`) Signed-off-by: Gilles Peskine --- scripts/ecp_comb_table.py | 237 --------- scripts/massif_max.pl | 36 -- tests/scripts/audit-validity-dates.py | 469 ------------------ tests/scripts/gen_ctr_drbg.pl | 96 ---- tests/scripts/gen_gcm_decrypt.pl | 101 ---- tests/scripts/gen_gcm_encrypt.pl | 84 ---- tests/scripts/gen_pkcs1_v21_sign_verify.pl | 74 --- tests/scripts/generate-afl-tests.sh | 71 --- tests/scripts/generate_server9_bad_saltlen.py | 87 ---- tests/scripts/run-metatests.sh | 89 ---- tests/scripts/run_demos.py | 65 --- tests/scripts/test_config_script.py | 175 ------- 12 files changed, 1584 deletions(-) delete mode 100755 scripts/ecp_comb_table.py delete mode 100755 scripts/massif_max.pl delete mode 100755 tests/scripts/audit-validity-dates.py delete mode 100755 tests/scripts/gen_ctr_drbg.pl delete mode 100755 tests/scripts/gen_gcm_decrypt.pl delete mode 100755 tests/scripts/gen_gcm_encrypt.pl delete mode 100755 tests/scripts/gen_pkcs1_v21_sign_verify.pl delete mode 100755 tests/scripts/generate-afl-tests.sh delete mode 100755 tests/scripts/generate_server9_bad_saltlen.py delete mode 100755 tests/scripts/run-metatests.sh delete mode 100755 tests/scripts/run_demos.py delete mode 100755 tests/scripts/test_config_script.py diff --git a/scripts/ecp_comb_table.py b/scripts/ecp_comb_table.py deleted file mode 100755 index 6146e881c9..0000000000 --- a/scripts/ecp_comb_table.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -""" -Purpose - -This script dumps comb table of ec curve. When you add a new ec curve, you -can use this script to generate codes to define `_T` in ecp_curves.c -""" - -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -import os -import subprocess -import sys -import tempfile - -HOW_TO_ADD_NEW_CURVE = """ -If you are trying to add new curve, you can follow these steps: - -1. Define curve parameters (_p, _gx, etc...) in ecp_curves.c. -2. Add a macro to define _T to NULL following these parameters. -3. Build mbedcrypto -4. Run this script with an argument of new curve -5. Copy the output of this script into ecp_curves.c and replace the macro added - in Step 2 -6. Rebuild and test if everything is ok - -Replace the in the above with the name of the curve you want to add.""" - -CC = os.getenv('CC', 'cc') -MBEDTLS_LIBRARY_PATH = os.getenv('MBEDTLS_LIBRARY_PATH', "library") - -SRC_DUMP_COMB_TABLE = r''' -#include -#include -#include "mbedtls/ecp.h" -#include "mbedtls/error.h" - -static void dump_mpi_initialize( const char *name, const mbedtls_mpi *d ) -{ - uint8_t buf[128] = {0}; - size_t olen; - uint8_t *p; - - olen = mbedtls_mpi_size( d ); - mbedtls_mpi_write_binary_le( d, buf, olen ); - printf("static const mbedtls_mpi_uint %s[] = {\n", name); - for (p = buf; p < buf + olen; p += 8) { - printf( " BYTES_TO_T_UINT_8( 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X ),\n", - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7] ); - } - printf("};\n"); -} - -static void dump_T( const mbedtls_ecp_group *grp ) -{ - char name[128]; - - printf( "#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1\n" ); - - for (size_t i = 0; i < grp->T_size; ++i) { - snprintf( name, sizeof(name), "%s_T_%zu_X", CURVE_NAME, i ); - dump_mpi_initialize( name, &grp->T[i].X ); - - snprintf( name, sizeof(name), "%s_T_%zu_Y", CURVE_NAME, i ); - dump_mpi_initialize( name, &grp->T[i].Y ); - } - printf( "static const mbedtls_ecp_point %s_T[%zu] = {\n", CURVE_NAME, grp->T_size ); - size_t olen; - for (size_t i = 0; i < grp->T_size; ++i) { - int z; - if ( mbedtls_mpi_cmp_int(&grp->T[i].Z, 0) == 0 ) { - z = 0; - } else if ( mbedtls_mpi_cmp_int(&grp->T[i].Z, 1) == 0 ) { - z = 1; - } else { - fprintf( stderr, "Unexpected value of Z (i = %d)\n", (int)i ); - exit( 1 ); - } - printf( " ECP_POINT_INIT_XY_Z%d(%s_T_%zu_X, %s_T_%zu_Y),\n", - z, - CURVE_NAME, i, - CURVE_NAME, i - ); - } - printf("};\n#endif\n\n"); -} - -int main() -{ - int rc; - mbedtls_mpi m; - mbedtls_ecp_point R; - mbedtls_ecp_group grp; - - mbedtls_ecp_group_init( &grp ); - rc = mbedtls_ecp_group_load( &grp, CURVE_ID ); - if (rc != 0) { - char buf[100]; - mbedtls_strerror( rc, buf, sizeof(buf) ); - fprintf( stderr, "mbedtls_ecp_group_load: %s (-0x%x)\n", buf, -rc ); - return 1; - } - grp.T = NULL; - mbedtls_ecp_point_init( &R ); - mbedtls_mpi_init( &m); - mbedtls_mpi_lset( &m, 1 ); - rc = mbedtls_ecp_mul( &grp, &R, &m, &grp.G, NULL, NULL ); - if ( rc != 0 ) { - char buf[100]; - mbedtls_strerror( rc, buf, sizeof(buf) ); - fprintf( stderr, "mbedtls_ecp_mul: %s (-0x%x)\n", buf, -rc ); - return 1; - } - if ( grp.T == NULL ) { - fprintf( stderr, "grp.T is not generated. Please make sure" - "MBEDTLS_ECP_FIXED_POINT_OPTIM is enabled in mbedtls_config.h\n" ); - return 1; - } - dump_T( &grp ); - return 0; -} -''' - -SRC_DUMP_KNOWN_CURVE = r''' -#include -#include -#include "mbedtls/ecp.h" - -int main() { - const mbedtls_ecp_curve_info *info = mbedtls_ecp_curve_list(); - mbedtls_ecp_group grp; - - mbedtls_ecp_group_init( &grp ); - while ( info->name != NULL ) { - mbedtls_ecp_group_load( &grp, info->grp_id ); - if ( mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS ) { - printf( " %s", info->name ); - } - info++; - } - printf( "\n" ); - return 0; -} -''' - - -def join_src_path(*args): - return os.path.normpath(os.path.join(os.path.dirname(__file__), "..", *args)) - - -def run_c_source(src, cflags): - """ - Compile and run C source code - :param src: the c language code to run - :param cflags: additional cflags passing to compiler - :return: - """ - binname = tempfile.mktemp(prefix="mbedtls") - fd, srcname = tempfile.mkstemp(prefix="mbedtls", suffix=".c") - srcfile = os.fdopen(fd, mode="w") - srcfile.write(src) - srcfile.close() - args = [CC, - *cflags, - '-I' + join_src_path("include"), - "-o", binname, - '-L' + MBEDTLS_LIBRARY_PATH, - srcname, - '-lmbedcrypto'] - - p = subprocess.run(args=args, check=False) - if p.returncode != 0: - return False - p = subprocess.run(args=[binname], check=False, env={ - 'LD_LIBRARY_PATH': MBEDTLS_LIBRARY_PATH - }) - if p.returncode != 0: - return False - os.unlink(srcname) - os.unlink(binname) - return True - - -def compute_curve(curve): - """compute comb table for curve""" - r = run_c_source( - SRC_DUMP_COMB_TABLE, - [ - '-g', - '-DCURVE_ID=MBEDTLS_ECP_DP_%s' % curve.upper(), - '-DCURVE_NAME="%s"' % curve.lower(), - ]) - if not r: - print("""\ -Unable to compile and run utility.""", file=sys.stderr) - sys.exit(1) - - -def usage(): - print(""" -Usage: python %s ... - -Arguments: - curve Specify one or more curve names (e.g secp256r1) - -All possible curves: """ % sys.argv[0]) - run_c_source(SRC_DUMP_KNOWN_CURVE, []) - print(""" -Environment Variable: - CC Specify which c compile to use to compile utility. - MBEDTLS_LIBRARY_PATH - Specify the path to mbedcrypto library. (e.g. build/library/) - -How to add a new curve: %s""" % HOW_TO_ADD_NEW_CURVE) - - -def run_main(): - shared_lib_path = os.path.normpath(os.path.join(MBEDTLS_LIBRARY_PATH, "libmbedcrypto.so")) - static_lib_path = os.path.normpath(os.path.join(MBEDTLS_LIBRARY_PATH, "libmbedcrypto.a")) - if not os.path.exists(shared_lib_path) and not os.path.exists(static_lib_path): - print("Warning: both '%s' and '%s' are not exists. This script will use " - "the library from your system instead of the library compiled by " - "this source directory.\n" - "You can specify library path using environment variable " - "'MBEDTLS_LIBRARY_PATH'." % (shared_lib_path, static_lib_path), - file=sys.stderr) - - if len(sys.argv) <= 1: - usage() - else: - for curve in sys.argv[1:]: - compute_curve(curve) - - -if __name__ == '__main__': - run_main() diff --git a/scripts/massif_max.pl b/scripts/massif_max.pl deleted file mode 100755 index 52ca606b52..0000000000 --- a/scripts/massif_max.pl +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env perl - -# Parse a massif.out.xxx file and output peak total memory usage -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -use warnings; -use strict; - -use utf8; -use open qw(:std utf8); - -die unless @ARGV == 1; - -my @snaps; -open my $fh, '<', $ARGV[0] or die; -{ local $/ = 'snapshot='; @snaps = <$fh>; } -close $fh or die; - -my ($max, $max_heap, $max_he, $max_stack) = (0, 0, 0, 0); -for (@snaps) -{ - my ($heap, $heap_extra, $stack) = m{ - mem_heap_B=(\d+)\n - mem_heap_extra_B=(\d+)\n - mem_stacks_B=(\d+) - }xm; - next unless defined $heap; - my $total = $heap + $heap_extra + $stack; - if( $total > $max ) { - ($max, $max_heap, $max_he, $max_stack) = ($total, $heap, $heap_extra, $stack); - } -} - -printf "$max (heap $max_heap+$max_he, stack $max_stack)\n"; diff --git a/tests/scripts/audit-validity-dates.py b/tests/scripts/audit-validity-dates.py deleted file mode 100755 index 3d0924602c..0000000000 --- a/tests/scripts/audit-validity-dates.py +++ /dev/null @@ -1,469 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -"""Audit validity date of X509 crt/crl/csr. - -This script is used to audit the validity date of crt/crl/csr used for testing. -It prints the information about X.509 objects excluding the objects that -are valid throughout the desired validity period. The data are collected -from framework/data_files/ and tests/suites/*.data files by default. -""" - -import os -import re -import typing -import argparse -import datetime -import glob -import logging -import hashlib -from enum import Enum - -# The script requires cryptography >= 35.0.0 which is only available -# for Python >= 3.6. -import cryptography -from cryptography import x509 - -from generate_test_code import FileWrapper - -import scripts_path # pylint: disable=unused-import -from mbedtls_framework import build_tree -from mbedtls_framework import logging_util - -def check_cryptography_version(): - match = re.match(r'^[0-9]+', cryptography.__version__) - if match is None or int(match.group(0)) < 35: - raise Exception("audit-validity-dates requires cryptography >= 35.0.0" - + "({} is too old)".format(cryptography.__version__)) - -class DataType(Enum): - CRT = 1 # Certificate - CRL = 2 # Certificate Revocation List - CSR = 3 # Certificate Signing Request - - -class DataFormat(Enum): - PEM = 1 # Privacy-Enhanced Mail - DER = 2 # Distinguished Encoding Rules - - -class AuditData: - """Store data location, type and validity period of X.509 objects.""" - #pylint: disable=too-few-public-methods - def __init__(self, data_type: DataType, x509_obj): - self.data_type = data_type - # the locations that the x509 object could be found - self.locations = [] # type: typing.List[str] - self.fill_validity_duration(x509_obj) - self._obj = x509_obj - encoding = cryptography.hazmat.primitives.serialization.Encoding.DER - self._identifier = hashlib.sha1(self._obj.public_bytes(encoding)).hexdigest() - - @property - def identifier(self): - """ - Identifier of the underlying X.509 object, which is consistent across - different runs. - """ - return self._identifier - - def fill_validity_duration(self, x509_obj): - """Read validity period from an X.509 object.""" - # Certificate expires after "not_valid_after" - # Certificate is invalid before "not_valid_before" - if self.data_type == DataType.CRT: - self.not_valid_after = x509_obj.not_valid_after - self.not_valid_before = x509_obj.not_valid_before - # CertificateRevocationList expires after "next_update" - # CertificateRevocationList is invalid before "last_update" - elif self.data_type == DataType.CRL: - self.not_valid_after = x509_obj.next_update - self.not_valid_before = x509_obj.last_update - # CertificateSigningRequest is always valid. - elif self.data_type == DataType.CSR: - self.not_valid_after = datetime.datetime.max - self.not_valid_before = datetime.datetime.min - else: - raise ValueError("Unsupported file_type: {}".format(self.data_type)) - - -class X509Parser: - """A parser class to parse crt/crl/csr file or data in PEM/DER format.""" - PEM_REGEX = br'-{5}BEGIN (?P.*?)-{5}(?P.*?)-{5}END (?P=type)-{5}' - PEM_TAG_REGEX = br'-{5}BEGIN (?P.*?)-{5}\n' - PEM_TAGS = { - DataType.CRT: 'CERTIFICATE', - DataType.CRL: 'X509 CRL', - DataType.CSR: 'CERTIFICATE REQUEST' - } - - def __init__(self, - backends: - typing.Dict[DataType, - typing.Dict[DataFormat, - typing.Callable[[bytes], object]]]) \ - -> None: - self.backends = backends - self.__generate_parsers() - - def __generate_parser(self, data_type: DataType): - """Parser generator for a specific DataType""" - tag = self.PEM_TAGS[data_type] - pem_loader = self.backends[data_type][DataFormat.PEM] - der_loader = self.backends[data_type][DataFormat.DER] - def wrapper(data: bytes): - pem_type = X509Parser.pem_data_type(data) - # It is in PEM format with target tag - if pem_type == tag: - return pem_loader(data) - # It is in PEM format without target tag - if pem_type: - return None - # It might be in DER format - try: - result = der_loader(data) - except ValueError: - result = None - return result - wrapper.__name__ = "{}.parser[{}]".format(type(self).__name__, tag) - return wrapper - - def __generate_parsers(self): - """Generate parsers for all support DataType""" - self.parsers = {} - for data_type, _ in self.PEM_TAGS.items(): - self.parsers[data_type] = self.__generate_parser(data_type) - - def __getitem__(self, item): - return self.parsers[item] - - @staticmethod - def pem_data_type(data: bytes) -> typing.Optional[str]: - """Get the tag from the data in PEM format - - :param data: data to be checked in binary mode. - :return: PEM tag or "" when no tag detected. - """ - m = re.search(X509Parser.PEM_TAG_REGEX, data) - if m is not None: - return m.group('type').decode('UTF-8') - else: - return None - - @staticmethod - def check_hex_string(hex_str: str) -> bool: - """Check if the hex string is possibly DER data.""" - hex_len = len(hex_str) - # At least 6 hex char for 3 bytes: Type + Length + Content - if hex_len < 6: - return False - # Check if Type (1 byte) is SEQUENCE. - if hex_str[0:2] != '30': - return False - # Check LENGTH (1 byte) value - content_len = int(hex_str[2:4], base=16) - consumed = 4 - if content_len in (128, 255): - # Indefinite or Reserved - return False - elif content_len > 127: - # Definite, Long - length_len = (content_len - 128) * 2 - content_len = int(hex_str[consumed:consumed+length_len], base=16) - consumed += length_len - # Check LENGTH - if hex_len != content_len * 2 + consumed: - return False - return True - - -class Auditor: - """ - A base class that uses X509Parser to parse files to a list of AuditData. - - A subclass must implement the following methods: - - collect_default_files: Return a list of file names that are defaultly - used for parsing (auditing). The list will be stored in - Auditor.default_files. - - parse_file: Method that parses a single file to a list of AuditData. - - A subclass may override the following methods: - - parse_bytes: Defaultly, it parses `bytes` that contains only one valid - X.509 data(DER/PEM format) to an X.509 object. - - walk_all: Defaultly, it iterates over all the files in the provided - file name list, calls `parse_file` for each file and stores the results - by extending the `results` passed to the function. - """ - def __init__(self, logger): - self.logger = logger - self.default_files = self.collect_default_files() - self.parser = X509Parser({ - DataType.CRT: { - DataFormat.PEM: x509.load_pem_x509_certificate, - DataFormat.DER: x509.load_der_x509_certificate - }, - DataType.CRL: { - DataFormat.PEM: x509.load_pem_x509_crl, - DataFormat.DER: x509.load_der_x509_crl - }, - DataType.CSR: { - DataFormat.PEM: x509.load_pem_x509_csr, - DataFormat.DER: x509.load_der_x509_csr - }, - }) - - def collect_default_files(self) -> typing.List[str]: - """Collect the default files for parsing.""" - raise NotImplementedError - - def parse_file(self, filename: str) -> typing.List[AuditData]: - """ - Parse a list of AuditData from file. - - :param filename: name of the file to parse. - :return list of AuditData parsed from the file. - """ - raise NotImplementedError - - def parse_bytes(self, data: bytes): - """Parse AuditData from bytes.""" - for data_type in list(DataType): - try: - result = self.parser[data_type](data) - except ValueError as val_error: - result = None - self.logger.warning(val_error) - if result is not None: - audit_data = AuditData(data_type, result) - return audit_data - return None - - def walk_all(self, - results: typing.Dict[str, AuditData], - file_list: typing.Optional[typing.List[str]] = None) \ - -> None: - """ - Iterate over all the files in the list and get audit data. The - results will be written to `results` passed to this function. - - :param results: The dictionary used to store the parsed - AuditData. The keys of this dictionary should - be the identifier of the AuditData. - """ - if file_list is None: - file_list = self.default_files - for filename in file_list: - data_list = self.parse_file(filename) - for d in data_list: - if d.identifier in results: - results[d.identifier].locations.extend(d.locations) - else: - results[d.identifier] = d - - @staticmethod - def find_test_dir(): - """Get the relative path for the Mbed TLS test directory.""" - return os.path.relpath(build_tree.guess_mbedtls_root() + '/tests') - - -class TestDataAuditor(Auditor): - """Class for auditing files in `framework/data_files/`""" - - def collect_default_files(self): - """Collect all files in `framework/data_files/`""" - test_data_glob = os.path.join(build_tree.guess_mbedtls_root(), - 'framework', 'data_files/**') - data_files = [f for f in glob.glob(test_data_glob, recursive=True) - if os.path.isfile(f)] - return data_files - - def parse_file(self, filename: str) -> typing.List[AuditData]: - """ - Parse a list of AuditData from data file. - - :param filename: name of the file to parse. - :return list of AuditData parsed from the file. - """ - with open(filename, 'rb') as f: - data = f.read() - - results = [] - # Try to parse all PEM blocks. - is_pem = False - for idx, m in enumerate(re.finditer(X509Parser.PEM_REGEX, data, flags=re.S), 1): - is_pem = True - result = self.parse_bytes(data[m.start():m.end()]) - if result is not None: - result.locations.append("{}#{}".format(filename, idx)) - results.append(result) - - # Might be DER format. - if not is_pem: - result = self.parse_bytes(data) - if result is not None: - result.locations.append("{}".format(filename)) - results.append(result) - - return results - - -def parse_suite_data(data_f): - """ - Parses .data file for test arguments that possiblly have a - valid X.509 data. If you need a more precise parser, please - use generate_test_code.parse_test_data instead. - - :param data_f: file object of the data file. - :return: Generator that yields test function argument list. - """ - for line in data_f: - line = line.strip() - # Skip comments - if line.startswith('#'): - continue - - # Check parameters line - match = re.search(r'\A\w+(.*:)?\"', line) - if match: - # Read test vectors - parts = re.split(r'(?[0-9a-fA-F]+)"', test_arg) - if not match: - continue - if not X509Parser.check_hex_string(match.group('data')): - continue - audit_data = self.parse_bytes(bytes.fromhex(match.group('data'))) - if audit_data is None: - continue - audit_data.locations.append("{}:{}:#{}".format(filename, - data_f.line_no, - idx + 1)) - audit_data_list.append(audit_data) - - return audit_data_list - - -def list_all(audit_data: AuditData): - for loc in audit_data.locations: - print("{}\t{:20}\t{:20}\t{:3}\t{}".format( - audit_data.identifier, - audit_data.not_valid_before.isoformat(timespec='seconds'), - audit_data.not_valid_after.isoformat(timespec='seconds'), - audit_data.data_type.name, - loc)) - - -def main(): - """ - Perform argument parsing. - """ - parser = argparse.ArgumentParser(description=__doc__) - - parser.add_argument('-a', '--all', - action='store_true', - help='list the information of all the files') - parser.add_argument('-v', '--verbose', - action='store_true', dest='verbose', - help='show logs') - parser.add_argument('--from', dest='start_date', - help=('Start of desired validity period (UTC, YYYY-MM-DD). ' - 'Default: today'), - metavar='DATE') - parser.add_argument('--to', dest='end_date', - help=('End of desired validity period (UTC, YYYY-MM-DD). ' - 'Default: --from'), - metavar='DATE') - parser.add_argument('--data-files', action='append', nargs='*', - help='data files to audit', - metavar='FILE') - parser.add_argument('--suite-data-files', action='append', nargs='*', - help='suite data files to audit', - metavar='FILE') - - args = parser.parse_args() - - # start main routine - # setup logger - logger = logging.getLogger() - logging_util.configure_logger(logger) - logger.setLevel(logging.DEBUG if args.verbose else logging.ERROR) - - td_auditor = TestDataAuditor(logger) - sd_auditor = SuiteDataAuditor(logger) - - data_files = [] - suite_data_files = [] - if args.data_files is None and args.suite_data_files is None: - data_files = td_auditor.default_files - suite_data_files = sd_auditor.default_files - else: - if args.data_files is not None: - data_files = [x for l in args.data_files for x in l] - if args.suite_data_files is not None: - suite_data_files = [x for l in args.suite_data_files for x in l] - - # validity period start date - if args.start_date: - start_date = datetime.datetime.fromisoformat(args.start_date) - else: - start_date = datetime.datetime.today() - # validity period end date - if args.end_date: - end_date = datetime.datetime.fromisoformat(args.end_date) - else: - end_date = start_date - - # go through all the files - audit_results = {} - td_auditor.walk_all(audit_results, data_files) - sd_auditor.walk_all(audit_results, suite_data_files) - - logger.info("Total: {} objects found!".format(len(audit_results))) - - # we filter out the files whose validity duration covers the provided - # duration. - filter_func = lambda d: (start_date < d.not_valid_before) or \ - (d.not_valid_after < end_date) - - sortby_end = lambda d: d.not_valid_after - - if args.all: - filter_func = None - - # filter and output the results - for d in sorted(filter(filter_func, audit_results.values()), key=sortby_end): - list_all(d) - - logger.debug("Done!") - -check_cryptography_version() -if __name__ == "__main__": - main() diff --git a/tests/scripts/gen_ctr_drbg.pl b/tests/scripts/gen_ctr_drbg.pl deleted file mode 100755 index ec5e5d8915..0000000000 --- a/tests/scripts/gen_ctr_drbg.pl +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env perl -# -# Based on NIST CTR_DRBG.rsp validation file -# Only uses AES-256-CTR cases that use a Derivation function -# and concats nonce and personalization for initialization. -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -use strict; - -my $file = shift; - -open(TEST_DATA, "$file") or die "Opening test cases '$file': $!"; - -sub get_suite_val($) -{ - my $name = shift; - my $val = ""; - - my $line = ; - ($val) = ($line =~ /\[$name\s\=\s(\w+)\]/); - - return $val; -} - -sub get_val($) -{ - my $name = shift; - my $val = ""; - my $line; - - while($line = ) - { - next if($line !~ /=/); - last; - } - - ($val) = ($line =~ /^$name = (\w+)/); - - return $val; -} - -my $cnt = 1;; -while (my $line = ) -{ - next if ($line !~ /^\[AES-256 use df/); - - my $PredictionResistanceStr = get_suite_val("PredictionResistance"); - my $PredictionResistance = 0; - $PredictionResistance = 1 if ($PredictionResistanceStr eq 'True'); - my $EntropyInputLen = get_suite_val("EntropyInputLen"); - my $NonceLen = get_suite_val("NonceLen"); - my $PersonalizationStringLen = get_suite_val("PersonalizationStringLen"); - my $AdditionalInputLen = get_suite_val("AdditionalInputLen"); - - for ($cnt = 0; $cnt < 15; $cnt++) - { - my $Count = get_val("COUNT"); - my $EntropyInput = get_val("EntropyInput"); - my $Nonce = get_val("Nonce"); - my $PersonalizationString = get_val("PersonalizationString"); - my $AdditionalInput1 = get_val("AdditionalInput"); - my $EntropyInputPR1 = get_val("EntropyInputPR") if ($PredictionResistance == 1); - my $EntropyInputReseed = get_val("EntropyInputReseed") if ($PredictionResistance == 0); - my $AdditionalInputReseed = get_val("AdditionalInputReseed") if ($PredictionResistance == 0); - my $AdditionalInput2 = get_val("AdditionalInput"); - my $EntropyInputPR2 = get_val("EntropyInputPR") if ($PredictionResistance == 1); - my $ReturnedBits = get_val("ReturnedBits"); - - if ($PredictionResistance == 1) - { - print("CTR_DRBG NIST Validation (AES-256 use df,$PredictionResistanceStr,$EntropyInputLen,$NonceLen,$PersonalizationStringLen,$AdditionalInputLen) #$Count\n"); - print("ctr_drbg_validate_pr"); - print(":\"$Nonce$PersonalizationString\""); - print(":\"$EntropyInput$EntropyInputPR1$EntropyInputPR2\""); - print(":\"$AdditionalInput1\""); - print(":\"$AdditionalInput2\""); - print(":\"$ReturnedBits\""); - print("\n\n"); - } - else - { - print("CTR_DRBG NIST Validation (AES-256 use df,$PredictionResistanceStr,$EntropyInputLen,$NonceLen,$PersonalizationStringLen,$AdditionalInputLen) #$Count\n"); - print("ctr_drbg_validate_nopr"); - print(":\"$Nonce$PersonalizationString\""); - print(":\"$EntropyInput$EntropyInputReseed\""); - print(":\"$AdditionalInput1\""); - print(":\"$AdditionalInputReseed\""); - print(":\"$AdditionalInput2\""); - print(":\"$ReturnedBits\""); - print("\n\n"); - } - } -} -close(TEST_DATA); diff --git a/tests/scripts/gen_gcm_decrypt.pl b/tests/scripts/gen_gcm_decrypt.pl deleted file mode 100755 index 30d45c307d..0000000000 --- a/tests/scripts/gen_gcm_decrypt.pl +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env perl -# -# Based on NIST gcmDecryptxxx.rsp validation files -# Only first 3 of every set used for compile time saving -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -use strict; - -my $file = shift; - -open(TEST_DATA, "$file") or die "Opening test cases '$file': $!"; - -sub get_suite_val($) -{ - my $name = shift; - my $val = ""; - - while(my $line = ) - { - next if ($line !~ /^\[/); - ($val) = ($line =~ /\[$name\s\=\s(\w+)\]/); - last; - } - - return $val; -} - -sub get_val($) -{ - my $name = shift; - my $val = ""; - my $line; - - while($line = ) - { - next if($line !~ /=/); - last; - } - - ($val) = ($line =~ /^$name = (\w+)/); - - return $val; -} - -sub get_val_or_fail($) -{ - my $name = shift; - my $val = "FAIL"; - my $line; - - while($line = ) - { - next if($line !~ /=/ && $line !~ /FAIL/); - last; - } - - ($val) = ($line =~ /^$name = (\w+)/) if ($line =~ /=/); - - return $val; -} - -my $cnt = 1;; -while (my $line = ) -{ - my $key_len = get_suite_val("Keylen"); - next if ($key_len !~ /\d+/); - my $iv_len = get_suite_val("IVlen"); - my $pt_len = get_suite_val("PTlen"); - my $add_len = get_suite_val("AADlen"); - my $tag_len = get_suite_val("Taglen"); - - for ($cnt = 0; $cnt < 3; $cnt++) - { - my $Count = get_val("Count"); - my $key = get_val("Key"); - my $iv = get_val("IV"); - my $ct = get_val("CT"); - my $add = get_val("AAD"); - my $tag = get_val("Tag"); - my $pt = get_val_or_fail("PT"); - - print("GCM NIST Validation (AES-$key_len,$iv_len,$pt_len,$add_len,$tag_len) #$Count\n"); - print("gcm_decrypt_and_verify"); - print(":\"$key\""); - print(":\"$ct\""); - print(":\"$iv\""); - print(":\"$add\""); - print(":$tag_len"); - print(":\"$tag\""); - print(":\"$pt\""); - print(":0"); - print("\n\n"); - } -} - -print("GCM Selftest\n"); -print("gcm_selftest:\n\n"); - -close(TEST_DATA); diff --git a/tests/scripts/gen_gcm_encrypt.pl b/tests/scripts/gen_gcm_encrypt.pl deleted file mode 100755 index b4f08494c0..0000000000 --- a/tests/scripts/gen_gcm_encrypt.pl +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env perl -# -# Based on NIST gcmEncryptIntIVxxx.rsp validation files -# Only first 3 of every set used for compile time saving -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -use strict; - -my $file = shift; - -open(TEST_DATA, "$file") or die "Opening test cases '$file': $!"; - -sub get_suite_val($) -{ - my $name = shift; - my $val = ""; - - while(my $line = ) - { - next if ($line !~ /^\[/); - ($val) = ($line =~ /\[$name\s\=\s(\w+)\]/); - last; - } - - return $val; -} - -sub get_val($) -{ - my $name = shift; - my $val = ""; - my $line; - - while($line = ) - { - next if($line !~ /=/); - last; - } - - ($val) = ($line =~ /^$name = (\w+)/); - - return $val; -} - -my $cnt = 1;; -while (my $line = ) -{ - my $key_len = get_suite_val("Keylen"); - next if ($key_len !~ /\d+/); - my $iv_len = get_suite_val("IVlen"); - my $pt_len = get_suite_val("PTlen"); - my $add_len = get_suite_val("AADlen"); - my $tag_len = get_suite_val("Taglen"); - - for ($cnt = 0; $cnt < 3; $cnt++) - { - my $Count = get_val("Count"); - my $key = get_val("Key"); - my $pt = get_val("PT"); - my $add = get_val("AAD"); - my $iv = get_val("IV"); - my $ct = get_val("CT"); - my $tag = get_val("Tag"); - - print("GCM NIST Validation (AES-$key_len,$iv_len,$pt_len,$add_len,$tag_len) #$Count\n"); - print("gcm_encrypt_and_tag"); - print(":\"$key\""); - print(":\"$pt\""); - print(":\"$iv\""); - print(":\"$add\""); - print(":\"$ct\""); - print(":$tag_len"); - print(":\"$tag\""); - print(":0"); - print("\n\n"); - } -} - -print("GCM Selftest\n"); -print("gcm_selftest:\n\n"); - -close(TEST_DATA); diff --git a/tests/scripts/gen_pkcs1_v21_sign_verify.pl b/tests/scripts/gen_pkcs1_v21_sign_verify.pl deleted file mode 100755 index fe2d3f5d37..0000000000 --- a/tests/scripts/gen_pkcs1_v21_sign_verify.pl +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env perl -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -use strict; - -my $file = shift; - -open(TEST_DATA, "$file") or die "Opening test cases '$file': $!"; - -sub get_val($$) -{ - my $str = shift; - my $name = shift; - my $val = ""; - - while(my $line = ) - { - next if($line !~ /^# $str/); - last; - } - - while(my $line = ) - { - last if($line eq "\r\n"); - $val .= $line; - } - - $val =~ s/[ \r\n]//g; - - return $val; -} - -my $state = 0; -my $val_n = ""; -my $val_e = ""; -my $val_p = ""; -my $val_q = ""; -my $mod = 0; -my $cnt = 1; -while (my $line = ) -{ - next if ($line !~ /^# Example/); - - ( $mod ) = ($line =~ /A (\d+)/); - $val_n = get_val("RSA modulus n", "N"); - $val_e = get_val("RSA public exponent e", "E"); - $val_p = get_val("Prime p", "P"); - $val_q = get_val("Prime q", "Q"); - - for(my $i = 1; $i <= 6; $i++) - { - my $val_m = get_val("Message to be", "M"); - my $val_salt = get_val("Salt", "Salt"); - my $val_sig = get_val("Signature", "Sig"); - - print("RSASSA-PSS Signature Example ${cnt}_${i}\n"); - print("pkcs1_rsassa_pss_sign:$mod:16:\"$val_p\":16:\"$val_q\":16:\"$val_n\":16:\"$val_e\":SIG_RSA_SHA1:MBEDTLS_MD_SHA1"); - print(":\"$val_m\""); - print(":\"$val_salt\""); - print(":\"$val_sig\":0"); - print("\n\n"); - - print("RSASSA-PSS Signature Example ${cnt}_${i} (verify)\n"); - print("pkcs1_rsassa_pss_verify:$mod:16:\"$val_n\":16:\"$val_e\":SIG_RSA_SHA1:MBEDTLS_MD_SHA1"); - print(":\"$val_m\""); - print(":\"$val_salt\""); - print(":\"$val_sig\":0"); - print("\n\n"); - } - $cnt++; -} -close(TEST_DATA); diff --git a/tests/scripts/generate-afl-tests.sh b/tests/scripts/generate-afl-tests.sh deleted file mode 100755 index d4ef0f3af1..0000000000 --- a/tests/scripts/generate-afl-tests.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh - -# This script splits the data test files containing the test cases into -# individual files (one test case per file) suitable for use with afl -# (American Fuzzy Lop). http://lcamtuf.coredump.cx/afl/ -# -# Usage: generate-afl-tests.sh -# - should be the path to one of the test suite files -# such as 'test_suite_rsa.data' -# -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -# Abort on errors -set -e - -if [ -z $1 ] -then - echo " [!] No test file specified" >&2 - echo "Usage: $0 " >&2 - exit 1 -fi - -SRC_FILEPATH=$(dirname $1)/$(basename $1) -TESTSUITE=$(basename $1 .data) - -THIS_DIR=$(basename $PWD) - -if [ -d ../library -a -d ../include -a -d ../tests -a $THIS_DIR == "tests" ]; -then :; -else - echo " [!] Must be run from Mbed TLS tests directory" >&2 - exit 1 -fi - -DEST_TESTCASE_DIR=$TESTSUITE-afl-tests -DEST_OUTPUT_DIR=$TESTSUITE-afl-out - -echo " [+] Creating output directories" >&2 - -if [ -e $DEST_OUTPUT_DIR/* ]; -then : - echo " [!] Test output files already exist." >&2 - exit 1 -else - mkdir -p $DEST_OUTPUT_DIR -fi - -if [ -e $DEST_TESTCASE_DIR/* ]; -then : - echo " [!] Test output files already exist." >&2 -else - mkdir -p $DEST_TESTCASE_DIR -fi - -echo " [+] Creating test cases" >&2 -cd $DEST_TESTCASE_DIR - -split -p '^\s*$' ../$SRC_FILEPATH - -for f in *; -do - # Strip out any blank lines (no trim on OS X) - sed '/^\s*$/d' $f >testcase_$f - rm $f -done - -cd .. - -echo " [+] Test cases in $DEST_TESTCASE_DIR" >&2 - diff --git a/tests/scripts/generate_server9_bad_saltlen.py b/tests/scripts/generate_server9_bad_saltlen.py deleted file mode 100755 index 9af4dd3b6d..0000000000 --- a/tests/scripts/generate_server9_bad_saltlen.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 -"""Generate server9-bad-saltlen.crt - -Generate a certificate signed with RSA-PSS, with an incorrect salt length. -""" - -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -import subprocess -import argparse -from asn1crypto import pem, x509, core #type: ignore #pylint: disable=import-error - -OPENSSL_RSA_PSS_CERT_COMMAND = r''' -openssl x509 -req -CA {ca_name}.crt -CAkey {ca_name}.key -set_serial 24 {ca_password} \ - {openssl_extfile} -days 3650 -outform DER -in {csr} \ - -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:{anounce_saltlen} \ - -sigopt rsa_mgf1_md:sha256 -''' -SIG_OPT = \ - r'-sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:{saltlen} -sigopt rsa_mgf1_md:sha256' -OPENSSL_RSA_PSS_DGST_COMMAND = r'''openssl dgst -sign {ca_name}.key {ca_password} \ - -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:{actual_saltlen} \ - -sigopt rsa_mgf1_md:sha256''' - - -def auto_int(x): - return int(x, 0) - - -def build_argparser(parser): - """Build argument parser""" - parser.description = __doc__ - parser.add_argument('--ca-name', type=str, required=True, - help='Basename of CA files') - parser.add_argument('--ca-password', type=str, - required=True, help='CA key file password') - parser.add_argument('--csr', type=str, required=True, - help='CSR file for generating certificate') - parser.add_argument('--openssl-extfile', type=str, - required=True, help='X905 v3 extension config file') - parser.add_argument('--anounce_saltlen', type=auto_int, - required=True, help='Announced salt length') - parser.add_argument('--actual_saltlen', type=auto_int, - required=True, help='Actual salt length') - parser.add_argument('--output', type=str, required=True) - - -def main(): - parser = argparse.ArgumentParser() - build_argparser(parser) - args = parser.parse_args() - - return generate(**vars(args)) - -def generate(**kwargs): - """Generate different salt length certificate file.""" - ca_password = kwargs.get('ca_password', '') - if ca_password: - kwargs['ca_password'] = r'-passin "pass:{ca_password}"'.format( - **kwargs) - else: - kwargs['ca_password'] = '' - extfile = kwargs.get('openssl_extfile', '') - if extfile: - kwargs['openssl_extfile'] = '-extfile {openssl_extfile}'.format( - **kwargs) - else: - kwargs['openssl_extfile'] = '' - - cmd = OPENSSL_RSA_PSS_CERT_COMMAND.format(**kwargs) - der_bytes = subprocess.check_output(cmd, shell=True) - target_certificate = x509.Certificate.load(der_bytes) - - cmd = OPENSSL_RSA_PSS_DGST_COMMAND.format(**kwargs) - #pylint: disable=unexpected-keyword-arg - der_bytes = subprocess.check_output(cmd, - input=target_certificate['tbs_certificate'].dump(), - shell=True) - - with open(kwargs.get('output'), 'wb') as f: - target_certificate['signature_value'] = core.OctetBitString(der_bytes) - f.write(pem.armor('CERTIFICATE', target_certificate.dump())) - - -if __name__ == '__main__': - main() diff --git a/tests/scripts/run-metatests.sh b/tests/scripts/run-metatests.sh deleted file mode 100755 index 22a302c62f..0000000000 --- a/tests/scripts/run-metatests.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/sh - -help () { - cat <&2 "$0: FATAL: programs/test/metatest not found" - exit 120 -fi - -LIST_ONLY= -while getopts hl OPTLET; do - case $OPTLET in - h) help; exit;; - l) LIST_ONLY=1;; - \?) help >&2; exit 120;; - esac -done -shift $((OPTIND - 1)) - -list_matches () { - while read name platform junk; do - for pattern in "$@"; do - case $platform in - $pattern) echo "$name"; break;; - esac - done - done -} - -count=0 -errors=0 -run_metatest () { - ret=0 - "$METATEST_PROGRAM" "$1" || ret=$? - if [ $ret -eq 0 ]; then - echo >&2 "$0: Unexpected success: $1" - errors=$((errors + 1)) - fi - count=$((count + 1)) -} - -# Don't pipe the output of metatest so that if it fails, this script exits -# immediately with a failure status. -full_list=$("$METATEST_PROGRAM" list) -matching_list=$(printf '%s\n' "$full_list" | list_matches "$@") - -if [ -n "$LIST_ONLY" ]; then - printf '%s\n' $matching_list - exit -fi - -for name in $matching_list; do - run_metatest "$name" -done - -if [ $errors -eq 0 ]; then - echo "Ran $count metatests, all good." - exit 0 -else - echo "Ran $count metatests, $errors unexpected successes." - exit 1 -fi diff --git a/tests/scripts/run_demos.py b/tests/scripts/run_demos.py deleted file mode 100755 index f9a8100141..0000000000 --- a/tests/scripts/run_demos.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -"""Run the Mbed TLS demo scripts. -""" -import argparse -import glob -import subprocess -import sys - -def run_demo(demo, quiet=False): - """Run the specified demo script. Return True if it succeeds.""" - args = {} - if quiet: - args['stdout'] = subprocess.DEVNULL - args['stderr'] = subprocess.DEVNULL - returncode = subprocess.call([demo], **args) - return returncode == 0 - -def run_demos(demos, quiet=False): - """Run the specified demos and print summary information about failures. - - Return True if all demos passed and False if a demo fails. - """ - failures = [] - for demo in demos: - if not quiet: - print('#### {} ####'.format(demo)) - success = run_demo(demo, quiet=quiet) - if not success: - failures.append(demo) - if not quiet: - print('{}: FAIL'.format(demo)) - if quiet: - print('{}: {}'.format(demo, 'PASS' if success else 'FAIL')) - else: - print('') - successes = len(demos) - len(failures) - print('{}/{} demos passed'.format(successes, len(demos))) - if failures and not quiet: - print('Failures:', *failures) - return not failures - -def run_all_demos(quiet=False): - """Run all the available demos. - - Return True if all demos passed and False if a demo fails. - """ - mbedtls_demos = glob.glob('programs/*/*_demo.sh') - tf_psa_crypto_demos = glob.glob('tf-psa-crypto/programs/*/*_demo.sh') - all_demos = mbedtls_demos + tf_psa_crypto_demos - if not all_demos: - # Keep the message on one line. pylint: disable=line-too-long - raise Exception('No demos found. run_demos needs to operate from the Mbed TLS toplevel directory.') - return run_demos(all_demos, quiet=quiet) - -def main(): - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--quiet', '-q', - action='store_true', - help="suppress the output of demos") - options = parser.parse_args() - success = run_all_demos(quiet=options.quiet) - sys.exit(0 if success else 1) - -if __name__ == '__main__': - main() diff --git a/tests/scripts/test_config_script.py b/tests/scripts/test_config_script.py deleted file mode 100755 index b58a3114cf..0000000000 --- a/tests/scripts/test_config_script.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python3 - -"""Test helper for the Mbed TLS configuration file tool - -Run config.py with various parameters and write the results to files. - -This is a harness to help regression testing, not a functional tester. -Sample usage: - - test_config_script.py -d old - ## Modify config.py and/or mbedtls_config.h ## - test_config_script.py -d new - diff -ru old new -""" - -## Copyright The Mbed TLS Contributors -## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -## - -import argparse -import glob -import os -import re -import shutil -import subprocess - -OUTPUT_FILE_PREFIX = 'config-' - -def output_file_name(directory, stem, extension): - return os.path.join(directory, - '{}{}.{}'.format(OUTPUT_FILE_PREFIX, - stem, extension)) - -def cleanup_directory(directory): - """Remove old output files.""" - for extension in []: - pattern = output_file_name(directory, '*', extension) - filenames = glob.glob(pattern) - for filename in filenames: - os.remove(filename) - -def prepare_directory(directory): - """Create the output directory if it doesn't exist yet. - - If there are old output files, remove them. - """ - if os.path.exists(directory): - cleanup_directory(directory) - else: - os.makedirs(directory) - -def guess_presets_from_help(help_text): - """Figure out what presets the script supports. - - help_text should be the output from running the script with --help. - """ - # Try the output format from config.py - hits = re.findall(r'\{([-\w,]+)\}', help_text) - for hit in hits: - words = set(hit.split(',')) - if 'get' in words and 'set' in words and 'unset' in words: - words.remove('get') - words.remove('set') - words.remove('unset') - return words - # Try the output format from config.pl - hits = re.findall(r'\n +([-\w]+) +- ', help_text) - if hits: - return hits - raise Exception("Unable to figure out supported presets. Pass the '-p' option.") - -def list_presets(options): - """Return the list of presets to test. - - The list is taken from the command line if present, otherwise it is - extracted from running the config script with --help. - """ - if options.presets: - return re.split(r'[ ,]+', options.presets) - else: - help_text = subprocess.run([options.script, '--help'], - check=False, # config.pl --help returns 255 - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).stdout - return guess_presets_from_help(help_text.decode('ascii')) - -def run_one(options, args, stem_prefix='', input_file=None): - """Run the config script with the given arguments. - - Take the original content from input_file if specified, defaulting - to options.input_file if input_file is None. - - Write the following files, where xxx contains stem_prefix followed by - a filename-friendly encoding of args: - * config-xxx.h: modified file. - * config-xxx.out: standard output. - * config-xxx.err: standard output. - * config-xxx.status: exit code. - - Return ("xxx+", "path/to/config-xxx.h") which can be used as - stem_prefix and input_file to call this function again with new args. - """ - if input_file is None: - input_file = options.input_file - stem = stem_prefix + '-'.join(args) - data_filename = output_file_name(options.output_directory, stem, 'h') - stdout_filename = output_file_name(options.output_directory, stem, 'out') - stderr_filename = output_file_name(options.output_directory, stem, 'err') - status_filename = output_file_name(options.output_directory, stem, 'status') - shutil.copy(input_file, data_filename) - # Pass only the file basename, not the full path, to avoid getting the - # directory name in error messages, which would make comparisons - # between output directories more difficult. - cmd = [os.path.abspath(options.script), - '-f', os.path.basename(data_filename)] - with open(stdout_filename, 'wb') as out: - with open(stderr_filename, 'wb') as err: - status = subprocess.call(cmd + args, - cwd=options.output_directory, - stdin=subprocess.DEVNULL, - stdout=out, stderr=err) - with open(status_filename, 'w') as status_file: - status_file.write('{}\n'.format(status)) - return stem + "+", data_filename - -### A list of symbols to test with. -### This script currently tests what happens when you change a symbol from -### having a value to not having a value or vice versa. This is not -### necessarily useful behavior, and we may not consider it a bug if -### config.py stops handling that case correctly. -TEST_SYMBOLS = [ - 'CUSTOM_SYMBOL', # does not exist - 'PSA_WANT_KEY_TYPE_AES', # set, no value - 'MBEDTLS_MPI_MAX_SIZE', # unset, has a value - 'MBEDTLS_NO_UDBL_DIVISION', # unset, in "System support" - 'MBEDTLS_PLATFORM_ZEROIZE_ALT', # unset, in "Customisation configuration options" -] - -def run_all(options): - """Run all the command lines to test.""" - presets = list_presets(options) - for preset in presets: - run_one(options, [preset]) - for symbol in TEST_SYMBOLS: - run_one(options, ['get', symbol]) - (stem, filename) = run_one(options, ['set', symbol]) - run_one(options, ['get', symbol], stem_prefix=stem, input_file=filename) - run_one(options, ['--force', 'set', symbol]) - (stem, filename) = run_one(options, ['set', symbol, 'value']) - run_one(options, ['get', symbol], stem_prefix=stem, input_file=filename) - run_one(options, ['--force', 'set', symbol, 'value']) - run_one(options, ['unset', symbol]) - -def main(): - """Command line entry point.""" - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument('-d', metavar='DIR', - dest='output_directory', required=True, - help="""Output directory.""") - parser.add_argument('-f', metavar='FILE', - dest='input_file', default='include/mbedtls/mbedtls_config.h', - help="""Config file (default: %(default)s).""") - parser.add_argument('-p', metavar='PRESET,...', - dest='presets', - help="""Presets to test (default: guessed from --help).""") - parser.add_argument('-s', metavar='FILE', - dest='script', default='scripts/config.py', - help="""Configuration script (default: %(default)s).""") - options = parser.parse_args() - prepare_directory(options.output_directory) - run_all(options) - -if __name__ == '__main__': - main() From 702b389645e482cf579737a72c44738555a95c55 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 3 Mar 2026 13:52:24 +0100 Subject: [PATCH 3/6] Update framework with moved scripts Signed-off-by: Gilles Peskine --- framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework b/framework index 9b92164c47..c3d6599465 160000 --- a/framework +++ b/framework @@ -1 +1 @@ -Subproject commit 9b92164c47fdaecb2600b417733507e2a105c3a5 +Subproject commit c3d659946503c3ef259bc424e1c3fd10d55df543 From c4d40c2de3bc704c12a7d7bcb88fd33a29ee51b3 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 26 Feb 2026 19:35:56 +0100 Subject: [PATCH 4/6] Move requirements to the framework for scripts in the framework Signed-off-by: Gilles Peskine --- scripts/ci.requirements.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/ci.requirements.txt b/scripts/ci.requirements.txt index 2ab7ba98da..7525036441 100644 --- a/scripts/ci.requirements.txt +++ b/scripts/ci.requirements.txt @@ -1,6 +1,7 @@ # Python package requirements for Mbed TLS testing. -r driver.requirements.txt +-r ../framework/scripts/ci.requirements.txt # The dependencies below are only used in scripts that we run on the Linux CI. @@ -16,13 +17,3 @@ pylint == 2.4.4; platform_system == 'Linux' # https://github.com/Mbed-TLS/mbedtls-framework/issues/50 # mypy 0.942 is the version in Ubuntu 22.04. mypy == 0.942; platform_system == 'Linux' - -# At the time of writing, only needed for tests/scripts/audit-validity-dates.py. -# It needs >=35.0.0 for correct operation, and that requires Python >=3.6. -# >=35.0.0 also requires Rust to build from source, which we are forced to do on -# FreeBSD, since PyPI doesn't carry binary wheels for the BSDs. -cryptography >= 35.0.0; platform_system == 'Linux' - -# For building `framework/data_files/server9-bad-saltlen.crt` and check python -# files. -asn1crypto; platform_system == 'Linux' From f840cb16e5bf8c289c631bddba039b04b9e4234f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 26 Feb 2026 19:36:33 +0100 Subject: [PATCH 5/6] Update paths after moving some scripts to the framework Signed-off-by: Gilles Peskine --- tests/scripts/components-configuration-crypto.sh | 2 +- tests/scripts/components-configuration.sh | 12 ++++++------ tests/scripts/components-platform.sh | 12 ++++++------ tests/scripts/components-sanitizers.sh | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/scripts/components-configuration-crypto.sh b/tests/scripts/components-configuration-crypto.sh index 2227287358..7683eec8d9 100644 --- a/tests/scripts/components-configuration-crypto.sh +++ b/tests/scripts/components-configuration-crypto.sh @@ -462,7 +462,7 @@ component_test_everest () { make test msg "test: metatests (clang, ASan)" - tests/scripts/run-metatests.sh any asan poison + framework/scripts/run-metatests.sh any asan poison msg "test: Everest ECDH context - ECDH-related part of ssl-opt.sh (ASan build)" # ~ 5s tests/ssl-opt.sh -f ECDH diff --git a/tests/scripts/components-configuration.sh b/tests/scripts/components-configuration.sh index dcd01c7e58..bad3822ccb 100644 --- a/tests/scripts/components-configuration.sh +++ b/tests/scripts/components-configuration.sh @@ -22,7 +22,7 @@ component_test_default_out_of_box () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~10s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } component_test_default_cmake_gcc_asan () { @@ -34,13 +34,13 @@ component_test_default_cmake_gcc_asan () { make test msg "program demos (ASan build)" # ~10s - tests/scripts/run_demos.py + framework/scripts/run_demos.py msg "test: selftest (ASan build)" # ~ 10s programs/test/selftest msg "test: metatests (GCC, ASan build)" - tests/scripts/run-metatests.sh any asan poison + framework/scripts/run-metatests.sh any asan poison msg "test: ssl-opt.sh (ASan build)" # ~ 1 min tests/ssl-opt.sh @@ -143,10 +143,10 @@ component_test_full_cmake_clang () { programs/test/cpp_dummy_build msg "test: metatests (clang)" - tests/scripts/run-metatests.sh any pthread + framework/scripts/run-metatests.sh any pthread msg "program demos (full config, clang)" # ~10s - tests/scripts/run_demos.py + framework/scripts/run_demos.py msg "test: psa_constant_names (full config, clang)" # ~ 1s $FRAMEWORK/scripts/test_psa_constant_names.py @@ -214,7 +214,7 @@ component_test_full_deprecated_warning () { $MAKE_COMMAND test msg "program demos: full config + MBEDTLS_TEST_DEPRECATED" # ~10s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } component_build_baremetal () { diff --git a/tests/scripts/components-platform.sh b/tests/scripts/components-platform.sh index b68a4aeafc..fe975fb3e8 100644 --- a/tests/scripts/components-platform.sh +++ b/tests/scripts/components-platform.sh @@ -330,7 +330,7 @@ component_test_arm_linux_gnueabi_gcc_arm5vte () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_arm_linux_gnueabi_gcc_arm5vte () { @@ -350,7 +350,7 @@ component_test_arm_linux_gnueabi_gcc_thumb_1_opt_0 () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_arm_linux_gnueabi_gcc_thumb_1_opt_0 () { @@ -368,7 +368,7 @@ component_test_arm_linux_gnueabi_gcc_thumb_1_opt_s () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_arm_linux_gnueabi_gcc_thumb_1_opt_s () { @@ -386,7 +386,7 @@ component_test_arm_linux_gnueabihf_gcc_armv7 () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_arm_linux_gnueabihf_gcc_armv7 () { @@ -404,7 +404,7 @@ component_test_arm_linux_gnueabihf_gcc_thumb_2 () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_arm_linux_gnueabihf_gcc_thumb_2 () { @@ -422,7 +422,7 @@ component_test_aarch64_linux_gnu_gcc () { programs/test/selftest msg "program demos: make, default config (out-of-box)" # ~0s - tests/scripts/run_demos.py + framework/scripts/run_demos.py } support_test_aarch64_linux_gnu_gcc () { diff --git a/tests/scripts/components-sanitizers.sh b/tests/scripts/components-sanitizers.sh index 26b149f69e..baed88aa53 100644 --- a/tests/scripts/components-sanitizers.sh +++ b/tests/scripts/components-sanitizers.sh @@ -132,10 +132,10 @@ component_test_memsan () { make test msg "test: metatests (MSan)" - tests/scripts/run-metatests.sh any msan + framework/scripts/run-metatests.sh any msan msg "program demos (MSan)" # ~20s - tests/scripts/run_demos.py + framework/scripts/run_demos.py msg "test: ssl-opt.sh (MSan)" # ~ 1 min tests/ssl-opt.sh From 931fc8c40687008c9b3e35994d1180236baa4e87 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Mar 2026 15:49:21 +0100 Subject: [PATCH 6/6] Update massif_max.pl location Signed-off-by: Gilles Peskine --- scripts/memory.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/memory.sh b/scripts/memory.sh index ffce225f2d..40b0de2d37 100755 --- a/scripts/memory.sh +++ b/scripts/memory.sh @@ -90,7 +90,7 @@ do_config() kill $SRV_PID wait $SRV_PID - scripts/massif_max.pl massif.out.* + framework/scripts/massif_max.pl massif.out.* mv massif.out.* massif-$NAME.$$ }