mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-07-07 12:21:11 +03:00
Added framework as a flattened directory
Signed-off-by: Minos Galanakis <minos.galanakis@arm.com>
This commit is contained in:
200
framework/scripts/mbedtls_framework/psa_test_case.py
Normal file
200
framework/scripts/mbedtls_framework/psa_test_case.py
Normal file
@ -0,0 +1,200 @@
|
||||
"""Generate test cases for PSA API calls, with automatic dependencies.
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import FrozenSet, List, Optional, Set
|
||||
|
||||
from . import build_tree
|
||||
from . import psa_information
|
||||
from . import test_case
|
||||
|
||||
|
||||
# Skip test cases for which the dependency symbols are not defined.
|
||||
# We assume that this means that a required mechanism is not implemented.
|
||||
# Note that if we erroneously skip generating test cases for
|
||||
# mechanisms that are not implemented, this should be caught
|
||||
# by the NOT_SUPPORTED test cases generated by generate_psa_tests.py
|
||||
# in test_suite_psa_crypto_not_supported and test_suite_psa_crypto_op_fail:
|
||||
# those emit tests with negative dependencies, which will not be skipped here.
|
||||
|
||||
def read_implemented_dependencies(acc: Set[str], filename: str) -> None:
|
||||
with open(filename) as input_stream:
|
||||
for line in input_stream:
|
||||
for symbol in re.findall(r'\bPSA_WANT_\w+\b', line):
|
||||
acc.add(symbol)
|
||||
|
||||
_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name
|
||||
|
||||
def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]:
|
||||
"""List the dependencies that are not implemented."""
|
||||
global _implemented_dependencies #pylint: disable=global-statement,invalid-name
|
||||
if _implemented_dependencies is None:
|
||||
# Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto
|
||||
# build system to build its crypto library. When it does, the first
|
||||
# case can just be removed.
|
||||
|
||||
if build_tree.looks_like_root('.'):
|
||||
if build_tree.looks_like_mbedtls_root('.') and \
|
||||
(not build_tree.is_mbedtls_3_6()):
|
||||
include_dir = 'tf-psa-crypto/include'
|
||||
else:
|
||||
include_dir = 'include'
|
||||
|
||||
acc = set() #type: Set[str]
|
||||
for filename in [
|
||||
os.path.join(include_dir, 'psa/crypto_config.h'),
|
||||
os.path.join(include_dir, 'psa/crypto_adjust_config_synonyms.h'),
|
||||
]:
|
||||
read_implemented_dependencies(acc, filename)
|
||||
_implemented_dependencies = frozenset(acc)
|
||||
return [dep
|
||||
for dep in dependencies
|
||||
if (dep not in _implemented_dependencies and
|
||||
dep.startswith('PSA_WANT'))]
|
||||
|
||||
|
||||
class TestCase(test_case.TestCase):
|
||||
"""A PSA test case with automatically inferred dependencies.
|
||||
|
||||
For mechanisms like ECC curves where the support status includes
|
||||
the key bit-size, this class assumes that only one bit-size is
|
||||
involved in a given test case.
|
||||
"""
|
||||
|
||||
def __init__(self, dependency_prefix: Optional[str] = None) -> None:
|
||||
"""Construct a test case for a PSA Crypto API call.
|
||||
|
||||
`dependency_prefix`: prefix to use in dependencies. Defaults to
|
||||
``'PSA_WANT_'``. Use ``'MBEDTLS_PSA_BUILTIN_'``
|
||||
when specifically testing builtin implementations.
|
||||
"""
|
||||
super().__init__()
|
||||
del self.dependencies
|
||||
self.manual_dependencies = [] #type: List[str]
|
||||
self.automatic_dependencies = set() #type: Set[str]
|
||||
self.dependency_prefix = dependency_prefix #type: Optional[str]
|
||||
self.negated_dependencies = set() #type: Set[str]
|
||||
self.key_bits = None #type: Optional[int]
|
||||
self.key_pair_usage = None #type: Optional[List[str]]
|
||||
|
||||
def set_key_bits(self, key_bits: Optional[int]) -> None:
|
||||
"""Use the given key size for automatic dependency generation.
|
||||
|
||||
Call this function before set_arguments() if relevant.
|
||||
|
||||
This is only relevant for ECC and DH keys. For other key types,
|
||||
this information is ignored.
|
||||
"""
|
||||
self.key_bits = key_bits
|
||||
|
||||
def set_key_pair_usage(self, key_pair_usage: Optional[List[str]]) -> None:
|
||||
"""Use the given suffixes for key pair dependencies.
|
||||
|
||||
Call this function before set_arguments() if relevant.
|
||||
|
||||
This is only relevant for key pair types. For other key types,
|
||||
this information is ignored.
|
||||
"""
|
||||
self.key_pair_usage = key_pair_usage
|
||||
|
||||
def infer_dependencies(self, arguments: List[str]) -> List[str]:
|
||||
"""Infer dependencies based on the test case arguments."""
|
||||
dependencies = psa_information.automatic_dependencies(*arguments,
|
||||
prefix=self.dependency_prefix)
|
||||
if self.key_bits is not None:
|
||||
dependencies = psa_information.finish_family_dependencies(dependencies,
|
||||
self.key_bits)
|
||||
if self.key_pair_usage is not None:
|
||||
dependencies = psa_information.fix_key_pair_dependencies(dependencies,
|
||||
self.key_pair_usage)
|
||||
if 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE' in dependencies and \
|
||||
'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE' not in self.negated_dependencies and \
|
||||
self.key_bits is not None:
|
||||
size_dependency = ('PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS <= ' +
|
||||
str(self.key_bits))
|
||||
dependencies.append(size_dependency)
|
||||
return dependencies
|
||||
|
||||
def assumes_not_supported(self, name: str) -> None:
|
||||
"""Negate the given mechanism for automatic dependency generation.
|
||||
|
||||
`name` can be either a dependency symbol (``PSA_WANT_xxx``) or
|
||||
a mechanism name (``PSA_KEY_TYPE_xxx``, etc.).
|
||||
|
||||
Call this function before set_arguments() for a test case that should
|
||||
run if the given mechanism is not supported.
|
||||
|
||||
Call modifiers such as set_key_bits() and set_key_pair_usage() before
|
||||
calling this method, if applicable.
|
||||
|
||||
A mechanism is a PSA_XXX symbol, e.g. PSA_KEY_TYPE_AES, PSA_ALG_HMAC,
|
||||
etc. For mechanisms like ECC curves where the support status includes
|
||||
the key bit-size, this class assumes that only one bit-size is
|
||||
involved in a given test case.
|
||||
"""
|
||||
if name.startswith('PSA_WANT_'):
|
||||
self.negated_dependencies.add(name)
|
||||
return
|
||||
if name == 'PSA_KEY_TYPE_RSA_KEY_PAIR' and \
|
||||
self.key_bits is not None and \
|
||||
self.key_pair_usage == ['GENERATE']:
|
||||
# When RSA key pair generation is not supported, it could be
|
||||
# due to the specific key size is out of range, or because
|
||||
# RSA key pair generation itself is not supported. Assume the
|
||||
# latter.
|
||||
dep = psa_information.psa_want_symbol(name, prefix=self.dependency_prefix)
|
||||
|
||||
self.negated_dependencies.add(dep + '_GENERATE')
|
||||
return
|
||||
dependencies = self.infer_dependencies([name])
|
||||
# * If we have more than one dependency to negate, the result would
|
||||
# say that all of the dependencies are disabled, which is not
|
||||
# a desirable outcome: the negation of (A and B) is (!A or !B),
|
||||
# not (!A and !B).
|
||||
# * If we have no dependency to negate, the result wouldn't be a
|
||||
# not-supported case.
|
||||
# Assert that we don't reach either such case.
|
||||
assert len(dependencies) == 1
|
||||
self.negated_dependencies.add(dependencies[0])
|
||||
|
||||
def set_arguments(self, arguments: List[str]) -> None:
|
||||
"""Set test case arguments and automatically infer dependencies."""
|
||||
super().set_arguments(arguments)
|
||||
dependencies = self.infer_dependencies(arguments)
|
||||
for i in range(len(dependencies)): #pylint: disable=consider-using-enumerate
|
||||
if dependencies[i] in self.negated_dependencies:
|
||||
dependencies[i] = '!' + dependencies[i]
|
||||
self.skip_if_any_not_implemented(dependencies)
|
||||
self.automatic_dependencies.update(dependencies)
|
||||
|
||||
def set_dependencies(self, dependencies: List[str]) -> None:
|
||||
"""Override any previously added automatic or manual dependencies.
|
||||
|
||||
Also override any previous instruction to skip the test case.
|
||||
"""
|
||||
self.manual_dependencies = dependencies
|
||||
self.automatic_dependencies.clear()
|
||||
self.skip_reasons = []
|
||||
|
||||
def add_dependencies(self, dependencies: List[str]) -> None:
|
||||
"""Add manual dependencies."""
|
||||
self.manual_dependencies += dependencies
|
||||
|
||||
def get_dependencies(self) -> List[str]:
|
||||
# Make the output independent of the order in which the dependencies
|
||||
# are calculated by the script. Also avoid duplicates. This makes
|
||||
# the output robust with respect to refactoring of the scripts.
|
||||
dependencies = set(self.manual_dependencies)
|
||||
dependencies.update(self.automatic_dependencies)
|
||||
return sorted(dependencies)
|
||||
|
||||
def skip_if_any_not_implemented(self, dependencies: List[str]) -> None:
|
||||
"""Skip the test case if any of the given dependencies is not implemented."""
|
||||
not_implemented = find_dependencies_not_implemented(dependencies)
|
||||
for dep in not_implemented:
|
||||
self.skip_because('not implemented: ' + dep)
|
Reference in New Issue
Block a user