1#!/usr/bin/env python3 2 3"""Analyze the test outcomes from a full CI run. 4 5This script can also run on outcomes from a partial run, but the results are 6less likely to be useful. 7""" 8 9import argparse 10import sys 11import traceback 12import re 13import subprocess 14import os 15import typing 16 17import check_test_cases 18 19 20# `ComponentOutcomes` is a named tuple which is defined as: 21# ComponentOutcomes( 22# successes = { 23# "<suite_case>", 24# ... 25# }, 26# failures = { 27# "<suite_case>", 28# ... 29# } 30# ) 31# suite_case = "<suite>;<case>" 32ComponentOutcomes = typing.NamedTuple('ComponentOutcomes', 33 [('successes', typing.Set[str]), 34 ('failures', typing.Set[str])]) 35 36# `Outcomes` is a representation of the outcomes file, 37# which defined as: 38# Outcomes = { 39# "<component>": ComponentOutcomes, 40# ... 41# } 42Outcomes = typing.Dict[str, ComponentOutcomes] 43 44 45class Results: 46 """Process analysis results.""" 47 48 def __init__(self): 49 self.error_count = 0 50 self.warning_count = 0 51 52 def new_section(self, fmt, *args, **kwargs): 53 self._print_line('\n*** ' + fmt + ' ***\n', *args, **kwargs) 54 55 def info(self, fmt, *args, **kwargs): 56 self._print_line('Info: ' + fmt, *args, **kwargs) 57 58 def error(self, fmt, *args, **kwargs): 59 self.error_count += 1 60 self._print_line('Error: ' + fmt, *args, **kwargs) 61 62 def warning(self, fmt, *args, **kwargs): 63 self.warning_count += 1 64 self._print_line('Warning: ' + fmt, *args, **kwargs) 65 66 @staticmethod 67 def _print_line(fmt, *args, **kwargs): 68 sys.stderr.write((fmt + '\n').format(*args, **kwargs)) 69 70def execute_reference_driver_tests(results: Results, ref_component: str, driver_component: str, \ 71 outcome_file: str) -> None: 72 """Run the tests specified in ref_component and driver_component. Results 73 are stored in the output_file and they will be used for the following 74 coverage analysis""" 75 results.new_section("Test {} and {}", ref_component, driver_component) 76 77 shell_command = "tests/scripts/all.sh --outcome-file " + outcome_file + \ 78 " " + ref_component + " " + driver_component 79 results.info("Running: {}", shell_command) 80 ret_val = subprocess.run(shell_command.split(), check=False).returncode 81 82 if ret_val != 0: 83 results.error("failed to run reference/driver components") 84 85def analyze_coverage(results: Results, outcomes: Outcomes, 86 allow_list: typing.List[str], full_coverage: bool) -> None: 87 """Check that all available test cases are executed at least once.""" 88 available = check_test_cases.collect_available_test_cases() 89 for suite_case in available: 90 hit = any(suite_case in comp_outcomes.successes or 91 suite_case in comp_outcomes.failures 92 for comp_outcomes in outcomes.values()) 93 94 if not hit and suite_case not in allow_list: 95 if full_coverage: 96 results.error('Test case not executed: {}', suite_case) 97 else: 98 results.warning('Test case not executed: {}', suite_case) 99 elif hit and suite_case in allow_list: 100 # Test Case should be removed from the allow list. 101 if full_coverage: 102 results.error('Allow listed test case was executed: {}', suite_case) 103 else: 104 results.warning('Allow listed test case was executed: {}', suite_case) 105 106def name_matches_pattern(name: str, str_or_re) -> bool: 107 """Check if name matches a pattern, that may be a string or regex. 108 - If the pattern is a string, name must be equal to match. 109 - If the pattern is a regex, name must fully match. 110 """ 111 # The CI's python is too old for re.Pattern 112 #if isinstance(str_or_re, re.Pattern): 113 if not isinstance(str_or_re, str): 114 return str_or_re.fullmatch(name) is not None 115 else: 116 return str_or_re == name 117 118def analyze_driver_vs_reference(results: Results, outcomes: Outcomes, 119 component_ref: str, component_driver: str, 120 ignored_suites: typing.List[str], ignored_tests=None) -> None: 121 """Check that all tests passing in the reference component are also 122 passing in the corresponding driver component. 123 Skip: 124 - full test suites provided in ignored_suites list 125 - only some specific test inside a test suite, for which the corresponding 126 output string is provided 127 """ 128 ref_outcomes = outcomes.get("component_" + component_ref) 129 driver_outcomes = outcomes.get("component_" + component_driver) 130 131 if ref_outcomes is None or driver_outcomes is None: 132 results.error("required components are missing: bad outcome file?") 133 return 134 135 if not ref_outcomes.successes: 136 results.error("no passing test in reference component: bad outcome file?") 137 return 138 139 for suite_case in ref_outcomes.successes: 140 # suite_case is like "test_suite_foo.bar;Description of test case" 141 (full_test_suite, test_string) = suite_case.split(';') 142 test_suite = full_test_suite.split('.')[0] # retrieve main part of test suite name 143 144 # Immediately skip fully-ignored test suites 145 if test_suite in ignored_suites or full_test_suite in ignored_suites: 146 continue 147 148 # For ignored test cases inside test suites, just remember and: 149 # don't issue an error if they're skipped with drivers, 150 # but issue an error if they're not (means we have a bad entry). 151 ignored = False 152 if full_test_suite in ignored_tests: 153 for str_or_re in ignored_tests[full_test_suite]: 154 if name_matches_pattern(test_string, str_or_re): 155 ignored = True 156 157 if not ignored and not suite_case in driver_outcomes.successes: 158 results.error("PASS -> SKIP/FAIL: {}", suite_case) 159 if ignored and suite_case in driver_outcomes.successes: 160 results.error("uselessly ignored: {}", suite_case) 161 162def analyze_outcomes(results: Results, outcomes: Outcomes, args) -> None: 163 """Run all analyses on the given outcome collection.""" 164 analyze_coverage(results, outcomes, args['allow_list'], 165 args['full_coverage']) 166 167def read_outcome_file(outcome_file: str) -> Outcomes: 168 """Parse an outcome file and return an outcome collection. 169 """ 170 outcomes = {} 171 with open(outcome_file, 'r', encoding='utf-8') as input_file: 172 for line in input_file: 173 (_platform, component, suite, case, result, _cause) = line.split(';') 174 # Note that `component` is not unique. If a test case passes on Linux 175 # and fails on FreeBSD, it'll end up in both the successes set and 176 # the failures set. 177 suite_case = ';'.join([suite, case]) 178 if component not in outcomes: 179 outcomes[component] = ComponentOutcomes(set(), set()) 180 if result == 'PASS': 181 outcomes[component].successes.add(suite_case) 182 elif result == 'FAIL': 183 outcomes[component].failures.add(suite_case) 184 185 return outcomes 186 187def do_analyze_coverage(results: Results, outcomes: Outcomes, args) -> None: 188 """Perform coverage analysis.""" 189 results.new_section("Analyze coverage") 190 analyze_outcomes(results, outcomes, args) 191 192def do_analyze_driver_vs_reference(results: Results, outcomes: Outcomes, args) -> None: 193 """Perform driver vs reference analyze.""" 194 results.new_section("Analyze driver {} vs reference {}", 195 args['component_driver'], args['component_ref']) 196 197 ignored_suites = ['test_suite_' + x for x in args['ignored_suites']] 198 199 analyze_driver_vs_reference(results, outcomes, 200 args['component_ref'], args['component_driver'], 201 ignored_suites, args['ignored_tests']) 202 203# List of tasks with a function that can handle this task and additional arguments if required 204KNOWN_TASKS = { 205 'analyze_coverage': { 206 'test_function': do_analyze_coverage, 207 'args': { 208 'allow_list': [ 209 # Algorithm not supported yet 210 'test_suite_psa_crypto_metadata;Asymmetric signature: pure EdDSA', 211 # Algorithm not supported yet 212 'test_suite_psa_crypto_metadata;Cipher: XTS', 213 ], 214 'full_coverage': False, 215 } 216 }, 217 # There are 2 options to use analyze_driver_vs_reference_xxx locally: 218 # 1. Run tests and then analysis: 219 # - tests/scripts/all.sh --outcome-file "$PWD/out.csv" <component_ref> <component_driver> 220 # - tests/scripts/analyze_outcomes.py out.csv analyze_driver_vs_reference_xxx 221 # 2. Let this script run both automatically: 222 # - tests/scripts/analyze_outcomes.py out.csv analyze_driver_vs_reference_xxx 223 'analyze_driver_vs_reference_hash': { 224 'test_function': do_analyze_driver_vs_reference, 225 'args': { 226 'component_ref': 'test_psa_crypto_config_reference_hash_use_psa', 227 'component_driver': 'test_psa_crypto_config_accel_hash_use_psa', 228 'ignored_suites': [ 229 'shax', 'mdx', # the software implementations that are being excluded 230 'md.psa', # purposefully depends on whether drivers are present 231 'psa_crypto_low_hash.generated', # testing the builtins 232 ], 233 'ignored_tests': { 234 'test_suite_platform': [ 235 # Incompatible with sanitizers (e.g. ASan). If the driver 236 # component uses a sanitizer but the reference component 237 # doesn't, we have a PASS vs SKIP mismatch. 238 'Check mbedtls_calloc overallocation', 239 ], 240 } 241 } 242 }, 243 'analyze_driver_vs_reference_hmac': { 244 'test_function': do_analyze_driver_vs_reference, 245 'args': { 246 'component_ref': 'test_psa_crypto_config_reference_hmac', 247 'component_driver': 'test_psa_crypto_config_accel_hmac', 248 'ignored_suites': [ 249 # These suites require legacy hash support, which is disabled 250 # in the accelerated component. 251 'shax', 'mdx', 252 # This suite tests builtins directly, but these are missing 253 # in the accelerated case. 254 'psa_crypto_low_hash.generated', 255 ], 256 'ignored_tests': { 257 'test_suite_md': [ 258 # Builtin HMAC is not supported in the accelerate component. 259 re.compile('.*HMAC.*'), 260 # Following tests make use of functions which are not available 261 # when MD_C is disabled, as it happens in the accelerated 262 # test component. 263 re.compile('generic .* Hash file .*'), 264 'MD list', 265 ], 266 'test_suite_md.psa': [ 267 # "legacy only" tests require hash algorithms to be NOT 268 # accelerated, but this of course false for the accelerated 269 # test component. 270 re.compile('PSA dispatch .* legacy only'), 271 ], 272 'test_suite_platform': [ 273 # Incompatible with sanitizers (e.g. ASan). If the driver 274 # component uses a sanitizer but the reference component 275 # doesn't, we have a PASS vs SKIP mismatch. 276 'Check mbedtls_calloc overallocation', 277 ], 278 } 279 } 280 }, 281 'analyze_driver_vs_reference_cipher_aead_cmac': { 282 'test_function': do_analyze_driver_vs_reference, 283 'args': { 284 'component_ref': 'test_psa_crypto_config_reference_cipher_aead_cmac', 285 'component_driver': 'test_psa_crypto_config_accel_cipher_aead_cmac', 286 # Modules replaced by drivers. 287 'ignored_suites': [ 288 # low-level (block/stream) cipher modules 289 'aes', 'aria', 'camellia', 'des', 'chacha20', 290 # AEAD modes and CMAC 291 'ccm', 'chachapoly', 'cmac', 'gcm', 292 # The Cipher abstraction layer 293 'cipher', 294 ], 295 'ignored_tests': { 296 # PEM decryption is not supported so far. 297 # The rest of PEM (write, unencrypted read) works though. 298 'test_suite_pem': [ 299 re.compile(r'PEM read .*(AES|DES|\bencrypt).*'), 300 ], 301 'test_suite_platform': [ 302 # Incompatible with sanitizers (e.g. ASan). If the driver 303 # component uses a sanitizer but the reference component 304 # doesn't, we have a PASS vs SKIP mismatch. 305 'Check mbedtls_calloc overallocation', 306 ], 307 # Following tests depend on AES_C/DES_C but are not about 308 # them really, just need to know some error code is there. 309 'test_suite_error': [ 310 'Low and high error', 311 'Single low error' 312 ], 313 # Similar to test_suite_error above. 314 'test_suite_version': [ 315 'Check for MBEDTLS_AES_C when already present', 316 ], 317 # The en/decryption part of PKCS#12 is not supported so far. 318 # The rest of PKCS#12 (key derivation) works though. 319 'test_suite_pkcs12': [ 320 re.compile(r'PBE Encrypt, .*'), 321 re.compile(r'PBE Decrypt, .*'), 322 ], 323 # The en/decryption part of PKCS#5 is not supported so far. 324 # The rest of PKCS#5 (PBKDF2) works though. 325 'test_suite_pkcs5': [ 326 re.compile(r'PBES2 Encrypt, .*'), 327 re.compile(r'PBES2 Decrypt .*'), 328 ], 329 # Encrypted keys are not supported so far. 330 # pylint: disable=line-too-long 331 'test_suite_pkparse': [ 332 'Key ASN1 (Encrypted key PKCS12, trailing garbage data)', 333 'Key ASN1 (Encrypted key PKCS5, trailing garbage data)', 334 re.compile(r'Parse (RSA|EC) Key .*\(.* ([Ee]ncrypted|password).*\)'), 335 ], 336 } 337 } 338 }, 339 'analyze_driver_vs_reference_ecp_light_only': { 340 'test_function': do_analyze_driver_vs_reference, 341 'args': { 342 'component_ref': 'test_psa_crypto_config_reference_ecc_ecp_light_only', 343 'component_driver': 'test_psa_crypto_config_accel_ecc_ecp_light_only', 344 'ignored_suites': [ 345 # Modules replaced by drivers 346 'ecdsa', 'ecdh', 'ecjpake', 347 ], 348 'ignored_tests': { 349 'test_suite_platform': [ 350 # Incompatible with sanitizers (e.g. ASan). If the driver 351 # component uses a sanitizer but the reference component 352 # doesn't, we have a PASS vs SKIP mismatch. 353 'Check mbedtls_calloc overallocation', 354 ], 355 # This test wants a legacy function that takes f_rng, p_rng 356 # arguments, and uses legacy ECDSA for that. The test is 357 # really about the wrapper around the PSA RNG, not ECDSA. 358 'test_suite_random': [ 359 'PSA classic wrapper: ECDSA signature (SECP256R1)', 360 ], 361 # In the accelerated test ECP_C is not set (only ECP_LIGHT is) 362 # so we must ignore disparities in the tests for which ECP_C 363 # is required. 364 'test_suite_ecp': [ 365 re.compile(r'ECP check public-private .*'), 366 re.compile(r'ECP calculate public: .*'), 367 re.compile(r'ECP gen keypair .*'), 368 re.compile(r'ECP point muladd .*'), 369 re.compile(r'ECP point multiplication .*'), 370 re.compile(r'ECP test vectors .*'), 371 ], 372 'test_suite_ssl': [ 373 # This deprecated function is only present when ECP_C is On. 374 'Test configuration of groups for DHE through mbedtls_ssl_conf_curves()', 375 ], 376 } 377 } 378 }, 379 'analyze_driver_vs_reference_no_ecp_at_all': { 380 'test_function': do_analyze_driver_vs_reference, 381 'args': { 382 'component_ref': 'test_psa_crypto_config_reference_ecc_no_ecp_at_all', 383 'component_driver': 'test_psa_crypto_config_accel_ecc_no_ecp_at_all', 384 'ignored_suites': [ 385 # Modules replaced by drivers 386 'ecp', 'ecdsa', 'ecdh', 'ecjpake', 387 ], 388 'ignored_tests': { 389 'test_suite_platform': [ 390 # Incompatible with sanitizers (e.g. ASan). If the driver 391 # component uses a sanitizer but the reference component 392 # doesn't, we have a PASS vs SKIP mismatch. 393 'Check mbedtls_calloc overallocation', 394 ], 395 # See ecp_light_only 396 'test_suite_random': [ 397 'PSA classic wrapper: ECDSA signature (SECP256R1)', 398 ], 399 'test_suite_pkparse': [ 400 # When PK_PARSE_C and ECP_C are defined then PK_PARSE_EC_COMPRESSED 401 # is automatically enabled in build_info.h (backward compatibility) 402 # even if it is disabled in config_psa_crypto_no_ecp_at_all(). As a 403 # consequence compressed points are supported in the reference 404 # component but not in the accelerated one, so they should be skipped 405 # while checking driver's coverage. 406 re.compile(r'Parse EC Key .*compressed\)'), 407 re.compile(r'Parse Public EC Key .*compressed\)'), 408 ], 409 # See ecp_light_only 410 'test_suite_ssl': [ 411 'Test configuration of groups for DHE through mbedtls_ssl_conf_curves()', 412 ], 413 } 414 } 415 }, 416 'analyze_driver_vs_reference_ecc_no_bignum': { 417 'test_function': do_analyze_driver_vs_reference, 418 'args': { 419 'component_ref': 'test_psa_crypto_config_reference_ecc_no_bignum', 420 'component_driver': 'test_psa_crypto_config_accel_ecc_no_bignum', 421 'ignored_suites': [ 422 # Modules replaced by drivers 423 'ecp', 'ecdsa', 'ecdh', 'ecjpake', 424 'bignum_core', 'bignum_random', 'bignum_mod', 'bignum_mod_raw', 425 'bignum.generated', 'bignum.misc', 426 ], 427 'ignored_tests': { 428 'test_suite_platform': [ 429 # Incompatible with sanitizers (e.g. ASan). If the driver 430 # component uses a sanitizer but the reference component 431 # doesn't, we have a PASS vs SKIP mismatch. 432 'Check mbedtls_calloc overallocation', 433 ], 434 # See ecp_light_only 435 'test_suite_random': [ 436 'PSA classic wrapper: ECDSA signature (SECP256R1)', 437 ], 438 # See no_ecp_at_all 439 'test_suite_pkparse': [ 440 re.compile(r'Parse EC Key .*compressed\)'), 441 re.compile(r'Parse Public EC Key .*compressed\)'), 442 ], 443 'test_suite_asn1parse': [ 444 'INTEGER too large for mpi', 445 ], 446 'test_suite_asn1write': [ 447 re.compile(r'ASN.1 Write mpi.*'), 448 ], 449 'test_suite_debug': [ 450 re.compile(r'Debug print mbedtls_mpi.*'), 451 ], 452 # See ecp_light_only 453 'test_suite_ssl': [ 454 'Test configuration of groups for DHE through mbedtls_ssl_conf_curves()', 455 ], 456 } 457 } 458 }, 459 'analyze_driver_vs_reference_ecc_ffdh_no_bignum': { 460 'test_function': do_analyze_driver_vs_reference, 461 'args': { 462 'component_ref': 'test_psa_crypto_config_reference_ecc_ffdh_no_bignum', 463 'component_driver': 'test_psa_crypto_config_accel_ecc_ffdh_no_bignum', 464 'ignored_suites': [ 465 # Modules replaced by drivers 466 'ecp', 'ecdsa', 'ecdh', 'ecjpake', 'dhm', 467 'bignum_core', 'bignum_random', 'bignum_mod', 'bignum_mod_raw', 468 'bignum.generated', 'bignum.misc', 469 ], 470 'ignored_tests': { 471 'test_suite_platform': [ 472 # Incompatible with sanitizers (e.g. ASan). If the driver 473 # component uses a sanitizer but the reference component 474 # doesn't, we have a PASS vs SKIP mismatch. 475 'Check mbedtls_calloc overallocation', 476 ], 477 # See ecp_light_only 478 'test_suite_random': [ 479 'PSA classic wrapper: ECDSA signature (SECP256R1)', 480 ], 481 # See no_ecp_at_all 482 'test_suite_pkparse': [ 483 re.compile(r'Parse EC Key .*compressed\)'), 484 re.compile(r'Parse Public EC Key .*compressed\)'), 485 ], 486 'test_suite_asn1parse': [ 487 'INTEGER too large for mpi', 488 ], 489 'test_suite_asn1write': [ 490 re.compile(r'ASN.1 Write mpi.*'), 491 ], 492 'test_suite_debug': [ 493 re.compile(r'Debug print mbedtls_mpi.*'), 494 ], 495 # See ecp_light_only 496 'test_suite_ssl': [ 497 'Test configuration of groups for DHE through mbedtls_ssl_conf_curves()', 498 ], 499 } 500 } 501 }, 502 'analyze_driver_vs_reference_ffdh_alg': { 503 'test_function': do_analyze_driver_vs_reference, 504 'args': { 505 'component_ref': 'test_psa_crypto_config_reference_ffdh', 506 'component_driver': 'test_psa_crypto_config_accel_ffdh', 507 'ignored_suites': ['dhm'], 508 'ignored_tests': { 509 'test_suite_platform': [ 510 # Incompatible with sanitizers (e.g. ASan). If the driver 511 # component uses a sanitizer but the reference component 512 # doesn't, we have a PASS vs SKIP mismatch. 513 'Check mbedtls_calloc overallocation', 514 ], 515 } 516 } 517 }, 518 'analyze_driver_vs_reference_tfm_config': { 519 'test_function': do_analyze_driver_vs_reference, 520 'args': { 521 'component_ref': 'test_tfm_config', 522 'component_driver': 'test_tfm_config_p256m_driver_accel_ec', 523 'ignored_suites': [ 524 # Modules replaced by drivers 525 'asn1parse', 'asn1write', 526 'ecp', 'ecdsa', 'ecdh', 'ecjpake', 527 'bignum_core', 'bignum_random', 'bignum_mod', 'bignum_mod_raw', 528 'bignum.generated', 'bignum.misc', 529 ], 530 'ignored_tests': { 531 'test_suite_platform': [ 532 # Incompatible with sanitizers (e.g. ASan). If the driver 533 # component uses a sanitizer but the reference component 534 # doesn't, we have a PASS vs SKIP mismatch. 535 'Check mbedtls_calloc overallocation', 536 ], 537 # See ecp_light_only 538 'test_suite_random': [ 539 'PSA classic wrapper: ECDSA signature (SECP256R1)', 540 ], 541 } 542 } 543 }, 544 'analyze_driver_vs_reference_rsa': { 545 'test_function': do_analyze_driver_vs_reference, 546 'args': { 547 'component_ref': 'test_psa_crypto_config_reference_rsa_crypto', 548 'component_driver': 'test_psa_crypto_config_accel_rsa_crypto', 549 'ignored_suites': [ 550 # Modules replaced by drivers. 551 'rsa', 'pkcs1_v15', 'pkcs1_v21', 552 # We temporarily don't care about PK stuff. 553 'pk', 'pkwrite', 'pkparse' 554 ], 555 'ignored_tests': { 556 'test_suite_platform': [ 557 # Incompatible with sanitizers (e.g. ASan). If the driver 558 # component uses a sanitizer but the reference component 559 # doesn't, we have a PASS vs SKIP mismatch. 560 'Check mbedtls_calloc overallocation', 561 ], 562 # Following tests depend on RSA_C but are not about 563 # them really, just need to know some error code is there. 564 'test_suite_error': [ 565 'Low and high error', 566 'Single high error' 567 ], 568 # Constant time operations only used for PKCS1_V15 569 'test_suite_constant_time': [ 570 re.compile(r'mbedtls_ct_zeroize_if .*'), 571 re.compile(r'mbedtls_ct_memmove_left .*') 572 ], 573 'test_suite_psa_crypto': [ 574 # We don't support generate_key_ext entry points 575 # in drivers yet. 576 re.compile(r'PSA generate key ext: RSA, e=.*'), 577 ], 578 } 579 } 580 }, 581 'analyze_block_cipher_dispatch': { 582 'test_function': do_analyze_driver_vs_reference, 583 'args': { 584 'component_ref': 'test_full_block_cipher_legacy_dispatch', 585 'component_driver': 'test_full_block_cipher_psa_dispatch', 586 'ignored_suites': [ 587 # Skipped in the accelerated component 588 'aes', 'aria', 'camellia', 589 # These require AES_C, ARIA_C or CAMELLIA_C to be enabled in 590 # order for the cipher module (actually cipher_wrapper) to work 591 # properly. However these symbols are disabled in the accelerated 592 # component so we ignore them. 593 'cipher.ccm', 'cipher.gcm', 'cipher.aes', 'cipher.aria', 594 'cipher.camellia', 595 ], 596 'ignored_tests': { 597 'test_suite_cmac': [ 598 # Following tests require AES_C/ARIA_C/CAMELLIA_C to be enabled, 599 # but these are not available in the accelerated component. 600 'CMAC null arguments', 601 re.compile('CMAC.* (AES|ARIA|Camellia).*'), 602 ], 603 'test_suite_cipher.padding': [ 604 # Following tests require AES_C/CAMELLIA_C to be enabled, 605 # but these are not available in the accelerated component. 606 re.compile('Set( non-existent)? padding with (AES|CAMELLIA).*'), 607 ], 608 'test_suite_pkcs5': [ 609 # The AES part of PKCS#5 PBES2 is not yet supported. 610 # The rest of PKCS#5 (PBKDF2) works, though. 611 re.compile(r'PBES2 .* AES-.*') 612 ], 613 'test_suite_pkparse': [ 614 # PEM (called by pkparse) requires AES_C in order to decrypt 615 # the key, but this is not available in the accelerated 616 # component. 617 re.compile('Parse RSA Key.*(password|AES-).*'), 618 ], 619 'test_suite_pem': [ 620 # Following tests require AES_C, but this is diabled in the 621 # accelerated component. 622 re.compile('PEM read .*AES.*'), 623 'PEM read (unknown encryption algorithm)', 624 ], 625 'test_suite_error': [ 626 # Following tests depend on AES_C but are not about them 627 # really, just need to know some error code is there. 628 'Single low error', 629 'Low and high error', 630 ], 631 'test_suite_version': [ 632 # Similar to test_suite_error above. 633 'Check for MBEDTLS_AES_C when already present', 634 ], 635 'test_suite_platform': [ 636 # Incompatible with sanitizers (e.g. ASan). If the driver 637 # component uses a sanitizer but the reference component 638 # doesn't, we have a PASS vs SKIP mismatch. 639 'Check mbedtls_calloc overallocation', 640 ], 641 } 642 } 643 } 644} 645 646def main(): 647 main_results = Results() 648 649 try: 650 parser = argparse.ArgumentParser(description=__doc__) 651 parser.add_argument('outcomes', metavar='OUTCOMES.CSV', 652 help='Outcome file to analyze') 653 parser.add_argument('specified_tasks', default='all', nargs='?', 654 help='Analysis to be done. By default, run all tasks. ' 655 'With one or more TASK, run only those. ' 656 'TASK can be the name of a single task or ' 657 'comma/space-separated list of tasks. ') 658 parser.add_argument('--list', action='store_true', 659 help='List all available tasks and exit.') 660 parser.add_argument('--require-full-coverage', action='store_true', 661 dest='full_coverage', help="Require all available " 662 "test cases to be executed and issue an error " 663 "otherwise. This flag is ignored if 'task' is " 664 "neither 'all' nor 'analyze_coverage'") 665 options = parser.parse_args() 666 667 if options.list: 668 for task in KNOWN_TASKS: 669 print(task) 670 sys.exit(0) 671 672 if options.specified_tasks == 'all': 673 tasks_list = KNOWN_TASKS.keys() 674 else: 675 tasks_list = re.split(r'[, ]+', options.specified_tasks) 676 for task in tasks_list: 677 if task not in KNOWN_TASKS: 678 sys.stderr.write('invalid task: {}\n'.format(task)) 679 sys.exit(2) 680 681 KNOWN_TASKS['analyze_coverage']['args']['full_coverage'] = options.full_coverage 682 683 # If the outcome file exists, parse it once and share the result 684 # among tasks to improve performance. 685 # Otherwise, it will be generated by execute_reference_driver_tests. 686 if not os.path.exists(options.outcomes): 687 if len(tasks_list) > 1: 688 sys.stderr.write("mutiple tasks found, please provide a valid outcomes file.\n") 689 sys.exit(2) 690 691 task_name = tasks_list[0] 692 task = KNOWN_TASKS[task_name] 693 if task['test_function'] != do_analyze_driver_vs_reference: # pylint: disable=comparison-with-callable 694 sys.stderr.write("please provide valid outcomes file for {}.\n".format(task_name)) 695 sys.exit(2) 696 697 execute_reference_driver_tests(main_results, 698 task['args']['component_ref'], 699 task['args']['component_driver'], 700 options.outcomes) 701 702 outcomes = read_outcome_file(options.outcomes) 703 704 for task in tasks_list: 705 test_function = KNOWN_TASKS[task]['test_function'] 706 test_args = KNOWN_TASKS[task]['args'] 707 test_function(main_results, outcomes, test_args) 708 709 main_results.info("Overall results: {} warnings and {} errors", 710 main_results.warning_count, main_results.error_count) 711 712 sys.exit(0 if (main_results.error_count == 0) else 1) 713 714 except Exception: # pylint: disable=broad-except 715 # Print the backtrace and exit explicitly with our chosen status. 716 traceback.print_exc() 717 sys.exit(120) 718 719if __name__ == '__main__': 720 main() 721