mirror of
https://github.com/yuzu-mirror/mbedtls.git
synced 2026-04-05 14:37:10 +00:00
Merge pull request #4214 from gilles-peskine-arm/psa-storage-format-test-types
PSA storage format test case generator
This commit is contained in:
commit
01196d0464
12 changed files with 2255 additions and 163 deletions
|
|
@ -106,4 +106,4 @@ check scripts/generate_query_config.pl programs/test/query_config.c
|
|||
check scripts/generate_features.pl library/version_features.c
|
||||
check scripts/generate_visualc_files.pl visualc/VS2010
|
||||
check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generated.c
|
||||
check tests/scripts/generate_psa_tests.py tests/suites/test_suite_psa_crypto_not_supported.generated.data
|
||||
check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate test data for PSA cryptographic mechanisms.
|
||||
|
||||
With no arguments, generate all test data. With non-option arguments,
|
||||
generate only the specified files.
|
||||
"""
|
||||
|
||||
# Copyright The Mbed TLS Contributors
|
||||
|
|
@ -21,11 +24,12 @@ import argparse
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import FrozenSet, Iterable, List, Optional, TypeVar
|
||||
from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
|
||||
|
||||
import scripts_path # pylint: disable=unused-import
|
||||
from mbedtls_dev import crypto_knowledge
|
||||
from mbedtls_dev import macro_collector
|
||||
from mbedtls_dev import psa_storage
|
||||
from mbedtls_dev import test_case
|
||||
|
||||
T = TypeVar('T') #pylint: disable=invalid-name
|
||||
|
|
@ -54,6 +58,18 @@ def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]:
|
|||
"""
|
||||
return [finish_family_dependency(dep, bits) for dep in dependencies]
|
||||
|
||||
def automatic_dependencies(*expressions: str) -> List[str]:
|
||||
"""Infer dependencies of a test case by looking for PSA_xxx symbols.
|
||||
|
||||
The arguments are strings which should be C expressions. Do not use
|
||||
string literals or comments as this function is not smart enough to
|
||||
skip them.
|
||||
"""
|
||||
used = set()
|
||||
for expr in expressions:
|
||||
used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
|
||||
return sorted(psa_want_symbol(name) for name in used)
|
||||
|
||||
# A temporary hack: at the time of writing, not all dependency symbols
|
||||
# are implemented yet. Skip test cases for which the dependency symbols are
|
||||
# not available. Once all dependency symbols are available, this hack must
|
||||
|
|
@ -69,41 +85,13 @@ def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
|
|||
for dep in dependencies):
|
||||
dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
|
||||
|
||||
def test_case_for_key_type_not_supported(
|
||||
verb: str, key_type: str, bits: int,
|
||||
dependencies: List[str],
|
||||
*args: str,
|
||||
param_descr: str = ''
|
||||
) -> test_case.TestCase:
|
||||
"""Return one test case exercising a key creation method
|
||||
for an unsupported key type or size.
|
||||
"""
|
||||
hack_dependencies_not_implemented(dependencies)
|
||||
tc = test_case.TestCase()
|
||||
short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
|
||||
adverb = 'not' if dependencies else 'never'
|
||||
if param_descr:
|
||||
adverb = param_descr + ' ' + adverb
|
||||
tc.set_description('PSA {} {} {}-bit {} supported'
|
||||
.format(verb, short_key_type, bits, adverb))
|
||||
tc.set_dependencies(dependencies)
|
||||
tc.set_function(verb + '_not_supported')
|
||||
tc.set_arguments([key_type] + list(args))
|
||||
return tc
|
||||
|
||||
class TestGenerator:
|
||||
"""Gather information and generate test data."""
|
||||
class Information:
|
||||
"""Gather information about PSA constructors."""
|
||||
|
||||
def __init__(self, options):
|
||||
self.test_suite_directory = self.get_option(options, 'directory',
|
||||
'tests/suites')
|
||||
def __init__(self) -> None:
|
||||
self.constructors = self.read_psa_interface()
|
||||
|
||||
@staticmethod
|
||||
def get_option(options, name: str, default: T) -> T:
|
||||
value = getattr(options, name, None)
|
||||
return default if value is None else value
|
||||
|
||||
@staticmethod
|
||||
def remove_unwanted_macros(
|
||||
constructors: macro_collector.PSAMacroCollector
|
||||
|
|
@ -126,14 +114,34 @@ class TestGenerator:
|
|||
self.remove_unwanted_macros(constructors)
|
||||
return constructors
|
||||
|
||||
def write_test_data_file(self, basename: str,
|
||||
test_cases: Iterable[test_case.TestCase]) -> None:
|
||||
"""Write the test cases to a .data file.
|
||||
|
||||
The output file is ``basename + '.data'`` in the test suite directory.
|
||||
"""
|
||||
filename = os.path.join(self.test_suite_directory, basename + '.data')
|
||||
test_case.write_data_file(filename, test_cases)
|
||||
def test_case_for_key_type_not_supported(
|
||||
verb: str, key_type: str, bits: int,
|
||||
dependencies: List[str],
|
||||
*args: str,
|
||||
param_descr: str = ''
|
||||
) -> test_case.TestCase:
|
||||
"""Return one test case exercising a key creation method
|
||||
for an unsupported key type or size.
|
||||
"""
|
||||
hack_dependencies_not_implemented(dependencies)
|
||||
tc = test_case.TestCase()
|
||||
short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
|
||||
adverb = 'not' if dependencies else 'never'
|
||||
if param_descr:
|
||||
adverb = param_descr + ' ' + adverb
|
||||
tc.set_description('PSA {} {} {}-bit {} supported'
|
||||
.format(verb, short_key_type, bits, adverb))
|
||||
tc.set_dependencies(dependencies)
|
||||
tc.set_function(verb + '_not_supported')
|
||||
tc.set_arguments([key_type] + list(args))
|
||||
return tc
|
||||
|
||||
class NotSupported:
|
||||
"""Generate test cases for when something is not supported."""
|
||||
|
||||
def __init__(self, info: Information) -> None:
|
||||
self.constructors = info.constructors
|
||||
|
||||
ALWAYS_SUPPORTED = frozenset([
|
||||
'PSA_KEY_TYPE_DERIVE',
|
||||
|
|
@ -144,7 +152,7 @@ class TestGenerator:
|
|||
kt: crypto_knowledge.KeyType,
|
||||
param: Optional[int] = None,
|
||||
param_descr: str = '',
|
||||
) -> List[test_case.TestCase]:
|
||||
) -> Iterator[test_case.TestCase]:
|
||||
"""Return test cases exercising key creation when the given type is unsupported.
|
||||
|
||||
If param is present and not None, emit test cases conditioned on this
|
||||
|
|
@ -154,7 +162,7 @@ class TestGenerator:
|
|||
if kt.name in self.ALWAYS_SUPPORTED:
|
||||
# Don't generate test cases for key types that are always supported.
|
||||
# They would be skipped in all configurations, which is noise.
|
||||
return []
|
||||
return
|
||||
import_dependencies = [('!' if param is None else '') +
|
||||
psa_want_symbol(kt.name)]
|
||||
if kt.params is not None:
|
||||
|
|
@ -165,56 +173,275 @@ class TestGenerator:
|
|||
generate_dependencies = []
|
||||
else:
|
||||
generate_dependencies = import_dependencies
|
||||
test_cases = []
|
||||
for bits in kt.sizes_to_test():
|
||||
test_cases.append(test_case_for_key_type_not_supported(
|
||||
yield test_case_for_key_type_not_supported(
|
||||
'import', kt.expression, bits,
|
||||
finish_family_dependencies(import_dependencies, bits),
|
||||
test_case.hex_string(kt.key_material(bits)),
|
||||
param_descr=param_descr,
|
||||
))
|
||||
)
|
||||
if not generate_dependencies and param is not None:
|
||||
# If generation is impossible for this key type, rather than
|
||||
# supported or not depending on implementation capabilities,
|
||||
# only generate the test case once.
|
||||
continue
|
||||
test_cases.append(test_case_for_key_type_not_supported(
|
||||
yield test_case_for_key_type_not_supported(
|
||||
'generate', kt.expression, bits,
|
||||
finish_family_dependencies(generate_dependencies, bits),
|
||||
str(bits),
|
||||
param_descr=param_descr,
|
||||
))
|
||||
)
|
||||
# To be added: derive
|
||||
return test_cases
|
||||
|
||||
def generate_not_supported(self) -> None:
|
||||
def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
|
||||
"""Generate test cases that exercise the creation of keys of unsupported types."""
|
||||
test_cases = []
|
||||
for key_type in sorted(self.constructors.key_types):
|
||||
kt = crypto_knowledge.KeyType(key_type)
|
||||
test_cases += self.test_cases_for_key_type_not_supported(kt)
|
||||
# To be added: parametrized key types FFDH
|
||||
yield from self.test_cases_for_key_type_not_supported(kt)
|
||||
for curve_family in sorted(self.constructors.ecc_curves):
|
||||
for constr in ('PSA_KEY_TYPE_ECC_KEY_PAIR',
|
||||
'PSA_KEY_TYPE_ECC_PUBLIC_KEY'):
|
||||
kt = crypto_knowledge.KeyType(constr, [curve_family])
|
||||
test_cases += self.test_cases_for_key_type_not_supported(
|
||||
yield from self.test_cases_for_key_type_not_supported(
|
||||
kt, param_descr='type')
|
||||
test_cases += self.test_cases_for_key_type_not_supported(
|
||||
yield from self.test_cases_for_key_type_not_supported(
|
||||
kt, 0, param_descr='curve')
|
||||
self.write_test_data_file(
|
||||
'test_suite_psa_crypto_not_supported.generated',
|
||||
test_cases)
|
||||
|
||||
def generate_all(self):
|
||||
self.generate_not_supported()
|
||||
|
||||
class StorageKey(psa_storage.Key):
|
||||
"""Representation of a key for storage format testing."""
|
||||
|
||||
def __init__(self, *, description: str, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.description = description #type: str
|
||||
|
||||
class StorageFormat:
|
||||
"""Storage format stability test cases."""
|
||||
|
||||
def __init__(self, info: Information, version: int, forward: bool) -> None:
|
||||
"""Prepare to generate test cases for storage format stability.
|
||||
|
||||
* `info`: information about the API. See the `Information` class.
|
||||
* `version`: the storage format version to generate test cases for.
|
||||
* `forward`: if true, generate forward compatibility test cases which
|
||||
save a key and check that its representation is as intended. Otherwise
|
||||
generate backward compatibility test cases which inject a key
|
||||
representation and check that it can be read and used.
|
||||
"""
|
||||
self.constructors = info.constructors
|
||||
self.version = version
|
||||
self.forward = forward
|
||||
|
||||
def make_test_case(self, key: StorageKey) -> test_case.TestCase:
|
||||
"""Construct a storage format test case for the given key.
|
||||
|
||||
If ``forward`` is true, generate a forward compatibility test case:
|
||||
create a key and validate that it has the expected representation.
|
||||
Otherwise generate a backward compatibility test case: inject the
|
||||
key representation into storage and validate that it can be read
|
||||
correctly.
|
||||
"""
|
||||
verb = 'save' if self.forward else 'read'
|
||||
tc = test_case.TestCase()
|
||||
tc.set_description('PSA storage {}: {}'.format(verb, key.description))
|
||||
dependencies = automatic_dependencies(
|
||||
key.lifetime.string, key.type.string,
|
||||
key.usage.string, key.alg.string, key.alg2.string,
|
||||
)
|
||||
dependencies = finish_family_dependencies(dependencies, key.bits)
|
||||
tc.set_dependencies(dependencies)
|
||||
tc.set_function('key_storage_' + verb)
|
||||
if self.forward:
|
||||
extra_arguments = []
|
||||
else:
|
||||
# Some test keys have the RAW_DATA type and attributes that don't
|
||||
# necessarily make sense. We do this to validate numerical
|
||||
# encodings of the attributes.
|
||||
# Raw data keys have no useful exercise anyway so there is no
|
||||
# loss of test coverage.
|
||||
exercise = key.type.string != 'PSA_KEY_TYPE_RAW_DATA'
|
||||
extra_arguments = ['1' if exercise else '0']
|
||||
tc.set_arguments([key.lifetime.string,
|
||||
key.type.string, str(key.bits),
|
||||
key.usage.string, key.alg.string, key.alg2.string,
|
||||
'"' + key.material.hex() + '"',
|
||||
'"' + key.hex() + '"',
|
||||
*extra_arguments])
|
||||
return tc
|
||||
|
||||
def key_for_usage_flags(
|
||||
self,
|
||||
usage_flags: List[str],
|
||||
short: Optional[str] = None
|
||||
) -> StorageKey:
|
||||
"""Construct a test key for the given key usage."""
|
||||
usage = ' | '.join(usage_flags) if usage_flags else '0'
|
||||
if short is None:
|
||||
short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
|
||||
description = 'usage: ' + short
|
||||
key = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=0, alg2=0,
|
||||
material=b'K',
|
||||
description=description)
|
||||
return key
|
||||
|
||||
def all_keys_for_usage_flags(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering usage flags."""
|
||||
known_flags = sorted(self.constructors.key_usage_flags)
|
||||
yield self.key_for_usage_flags(['0'])
|
||||
for usage_flag in known_flags:
|
||||
yield self.key_for_usage_flags([usage_flag])
|
||||
for flag1, flag2 in zip(known_flags,
|
||||
known_flags[1:] + [known_flags[0]]):
|
||||
yield self.key_for_usage_flags([flag1, flag2])
|
||||
yield self.key_for_usage_flags(known_flags, short='all known')
|
||||
|
||||
def keys_for_type(
|
||||
self,
|
||||
key_type: str,
|
||||
params: Optional[Iterable[str]] = None
|
||||
) -> Iterator[StorageKey]:
|
||||
"""Generate test keys for the given key type.
|
||||
|
||||
For key types that depend on a parameter (e.g. elliptic curve family),
|
||||
`param` is the parameter to pass to the constructor. Only a single
|
||||
parameter is supported.
|
||||
"""
|
||||
kt = crypto_knowledge.KeyType(key_type, params)
|
||||
for bits in kt.sizes_to_test():
|
||||
usage_flags = 'PSA_KEY_USAGE_EXPORT'
|
||||
alg = 0
|
||||
alg2 = 0
|
||||
key_material = kt.key_material(bits)
|
||||
short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
|
||||
r'',
|
||||
kt.expression)
|
||||
description = 'type: {} {}-bit'.format(short_expression, bits)
|
||||
key = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type=kt.expression, bits=bits,
|
||||
usage=usage_flags, alg=alg, alg2=alg2,
|
||||
material=key_material,
|
||||
description=description)
|
||||
yield key
|
||||
|
||||
def all_keys_for_types(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering key types and their representations."""
|
||||
for key_type in sorted(self.constructors.key_types):
|
||||
yield from self.keys_for_type(key_type)
|
||||
for key_type in sorted(self.constructors.key_types_from_curve):
|
||||
for curve in sorted(self.constructors.ecc_curves):
|
||||
yield from self.keys_for_type(key_type, [curve])
|
||||
## Diffie-Hellman (FFDH) is not supported yet, either in
|
||||
## crypto_knowledge.py or in Mbed TLS.
|
||||
# for key_type in sorted(self.constructors.key_types_from_group):
|
||||
# for group in sorted(self.constructors.dh_groups):
|
||||
# yield from self.keys_for_type(key_type, [group])
|
||||
|
||||
def keys_for_algorithm(self, alg: str) -> Iterator[StorageKey]:
|
||||
"""Generate test keys for the specified algorithm."""
|
||||
# For now, we don't have information on the compatibility of key
|
||||
# types and algorithms. So we just test the encoding of algorithms,
|
||||
# and not that operations can be performed with them.
|
||||
descr = alg
|
||||
usage = 'PSA_KEY_USAGE_EXPORT'
|
||||
key1 = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=alg, alg2=0,
|
||||
material=b'K',
|
||||
description='alg: ' + descr)
|
||||
yield key1
|
||||
key2 = StorageKey(version=self.version,
|
||||
id=1, lifetime=0x00000001,
|
||||
type='PSA_KEY_TYPE_RAW_DATA', bits=8,
|
||||
usage=usage, alg=0, alg2=alg,
|
||||
material=b'L',
|
||||
description='alg2: ' + descr)
|
||||
yield key2
|
||||
|
||||
def all_keys_for_algorithms(self) -> Iterator[StorageKey]:
|
||||
"""Generate test keys covering algorithm encodings."""
|
||||
for alg in sorted(self.constructors.algorithms):
|
||||
yield from self.keys_for_algorithm(alg)
|
||||
# To do: algorithm constructors with parameters
|
||||
|
||||
def all_test_cases(self) -> Iterator[test_case.TestCase]:
|
||||
"""Generate all storage format test cases."""
|
||||
for key in self.all_keys_for_usage_flags():
|
||||
yield self.make_test_case(key)
|
||||
for key in self.all_keys_for_types():
|
||||
yield self.make_test_case(key)
|
||||
for key in self.all_keys_for_algorithms():
|
||||
yield self.make_test_case(key)
|
||||
# To do: vary id, lifetime
|
||||
|
||||
|
||||
class TestGenerator:
|
||||
"""Generate test data."""
|
||||
|
||||
def __init__(self, options) -> None:
|
||||
self.test_suite_directory = self.get_option(options, 'directory',
|
||||
'tests/suites')
|
||||
self.info = Information()
|
||||
|
||||
@staticmethod
|
||||
def get_option(options, name: str, default: T) -> T:
|
||||
value = getattr(options, name, None)
|
||||
return default if value is None else value
|
||||
|
||||
def filename_for(self, basename: str) -> str:
|
||||
"""The location of the data file with the specified base name."""
|
||||
return os.path.join(self.test_suite_directory, basename + '.data')
|
||||
|
||||
def write_test_data_file(self, basename: str,
|
||||
test_cases: Iterable[test_case.TestCase]) -> None:
|
||||
"""Write the test cases to a .data file.
|
||||
|
||||
The output file is ``basename + '.data'`` in the test suite directory.
|
||||
"""
|
||||
filename = self.filename_for(basename)
|
||||
test_case.write_data_file(filename, test_cases)
|
||||
|
||||
TARGETS = {
|
||||
'test_suite_psa_crypto_not_supported.generated':
|
||||
lambda info: NotSupported(info).test_cases_for_not_supported(),
|
||||
'test_suite_psa_crypto_storage_format.current':
|
||||
lambda info: StorageFormat(info, 0, True).all_test_cases(),
|
||||
'test_suite_psa_crypto_storage_format.v0':
|
||||
lambda info: StorageFormat(info, 0, False).all_test_cases(),
|
||||
} #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
|
||||
|
||||
def generate_target(self, name: str) -> None:
|
||||
test_cases = self.TARGETS[name](self.info)
|
||||
self.write_test_data_file(name, test_cases)
|
||||
|
||||
def main(args):
|
||||
"""Command line entry point."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--list', action='store_true',
|
||||
help='List available targets and exit')
|
||||
parser.add_argument('targets', nargs='*', metavar='TARGET',
|
||||
help='Target file to generate (default: all; "-": none)')
|
||||
options = parser.parse_args(args)
|
||||
generator = TestGenerator(options)
|
||||
generator.generate_all()
|
||||
if options.list:
|
||||
for name in sorted(generator.TARGETS):
|
||||
print(generator.filename_for(name))
|
||||
return
|
||||
if options.targets:
|
||||
# Allow "-" as a special case so you can run
|
||||
# ``generate_psa_tests.py - $targets`` and it works uniformly whether
|
||||
# ``$targets`` is empty or not.
|
||||
options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
|
||||
for target in options.targets
|
||||
if target != '-']
|
||||
else:
|
||||
options.targets = sorted(generator.TARGETS)
|
||||
for target in options.targets:
|
||||
generator.generate_target(target)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ or 1 (with a Python backtrace) if there was an operational error.
|
|||
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
|
@ -32,6 +31,7 @@ import sys
|
|||
|
||||
import scripts_path # pylint: disable=unused-import
|
||||
from mbedtls_dev import c_build_helper
|
||||
from mbedtls_dev import macro_collector
|
||||
|
||||
class ReadFileLineException(Exception):
|
||||
def __init__(self, filename, line_number):
|
||||
|
|
@ -78,7 +78,7 @@ class read_file_lines:
|
|||
raise ReadFileLineException(self.filename, self.line_number) \
|
||||
from exc_value
|
||||
|
||||
class Inputs:
|
||||
class InputsForTest(macro_collector.PSAMacroEnumerator):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Accumulate information about macros to test.
|
||||
|
||||
|
|
@ -87,27 +87,29 @@ class Inputs:
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.all_declared = set()
|
||||
# Sets of names per type
|
||||
self.statuses = set(['PSA_SUCCESS'])
|
||||
self.algorithms = set(['0xffffffff'])
|
||||
self.ecc_curves = set(['0xff'])
|
||||
self.dh_groups = set(['0xff'])
|
||||
self.key_types = set(['0xffff'])
|
||||
self.key_usage_flags = set(['0x80000000'])
|
||||
self.statuses.add('PSA_SUCCESS')
|
||||
self.algorithms.add('0xffffffff')
|
||||
self.ecc_curves.add('0xff')
|
||||
self.dh_groups.add('0xff')
|
||||
self.key_types.add('0xffff')
|
||||
self.key_usage_flags.add('0x80000000')
|
||||
|
||||
# Hard-coded values for unknown algorithms
|
||||
#
|
||||
# These have to have values that are correct for their respective
|
||||
# PSA_ALG_IS_xxx macros, but are also not currently assigned and are
|
||||
# not likely to be assigned in the near future.
|
||||
self.hash_algorithms = set(['0x020000fe']) # 0x020000ff is PSA_ALG_ANY_HASH
|
||||
self.mac_algorithms = set(['0x03007fff'])
|
||||
self.ka_algorithms = set(['0x09fc0000'])
|
||||
self.kdf_algorithms = set(['0x080000ff'])
|
||||
self.hash_algorithms.add('0x020000fe') # 0x020000ff is PSA_ALG_ANY_HASH
|
||||
self.mac_algorithms.add('0x03007fff')
|
||||
self.ka_algorithms.add('0x09fc0000')
|
||||
self.kdf_algorithms.add('0x080000ff')
|
||||
# For AEAD algorithms, the only variability is over the tag length,
|
||||
# and this only applies to known algorithms, so don't test an
|
||||
# unknown algorithm.
|
||||
self.aead_algorithms = set()
|
||||
|
||||
# Identifier prefixes
|
||||
self.table_by_prefix = {
|
||||
'ERROR': self.statuses,
|
||||
|
|
@ -140,15 +142,10 @@ class Inputs:
|
|||
'asymmetric_encryption_algorithm': [],
|
||||
'other_algorithm': [],
|
||||
}
|
||||
# macro name -> list of argument names
|
||||
self.argspecs = {}
|
||||
# argument name -> list of values
|
||||
self.arguments_for = {
|
||||
'mac_length': ['1', '63'],
|
||||
'tag_length': ['1', '63'],
|
||||
'min_mac_length': ['1', '63'],
|
||||
'min_tag_length': ['1', '63'],
|
||||
}
|
||||
self.arguments_for['mac_length'] += ['1', '63']
|
||||
self.arguments_for['min_mac_length'] += ['1', '63']
|
||||
self.arguments_for['tag_length'] += ['1', '63']
|
||||
self.arguments_for['min_tag_length'] += ['1', '63']
|
||||
|
||||
def get_names(self, type_word):
|
||||
"""Return the set of known names of values of the given type."""
|
||||
|
|
@ -161,62 +158,6 @@ class Inputs:
|
|||
'key_usage': self.key_usage_flags,
|
||||
}[type_word]
|
||||
|
||||
def gather_arguments(self):
|
||||
"""Populate the list of values for macro arguments.
|
||||
|
||||
Call this after parsing all the inputs.
|
||||
"""
|
||||
self.arguments_for['hash_alg'] = sorted(self.hash_algorithms)
|
||||
self.arguments_for['mac_alg'] = sorted(self.mac_algorithms)
|
||||
self.arguments_for['ka_alg'] = sorted(self.ka_algorithms)
|
||||
self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms)
|
||||
self.arguments_for['aead_alg'] = sorted(self.aead_algorithms)
|
||||
self.arguments_for['curve'] = sorted(self.ecc_curves)
|
||||
self.arguments_for['group'] = sorted(self.dh_groups)
|
||||
|
||||
@staticmethod
|
||||
def _format_arguments(name, arguments):
|
||||
"""Format a macro call with arguments.."""
|
||||
return name + '(' + ', '.join(arguments) + ')'
|
||||
|
||||
def distribute_arguments(self, name):
|
||||
"""Generate macro calls with each tested argument set.
|
||||
|
||||
If name is a macro without arguments, just yield "name".
|
||||
If name is a macro with arguments, yield a series of
|
||||
"name(arg1,...,argN)" where each argument takes each possible
|
||||
value at least once.
|
||||
"""
|
||||
try:
|
||||
if name not in self.argspecs:
|
||||
yield name
|
||||
return
|
||||
argspec = self.argspecs[name]
|
||||
if argspec == []:
|
||||
yield name + '()'
|
||||
return
|
||||
argument_lists = [self.arguments_for[arg] for arg in argspec]
|
||||
arguments = [values[0] for values in argument_lists]
|
||||
yield self._format_arguments(name, arguments)
|
||||
# Dear Pylint, enumerate won't work here since we're modifying
|
||||
# the array.
|
||||
# pylint: disable=consider-using-enumerate
|
||||
for i in range(len(arguments)):
|
||||
for value in argument_lists[i][1:]:
|
||||
arguments[i] = value
|
||||
yield self._format_arguments(name, arguments)
|
||||
arguments[i] = argument_lists[0][0]
|
||||
except BaseException as e:
|
||||
raise Exception('distribute_arguments({})'.format(name)) from e
|
||||
|
||||
def generate_expressions(self, names):
|
||||
return itertools.chain(*map(self.distribute_arguments, names))
|
||||
|
||||
_argument_split_re = re.compile(r' *, *')
|
||||
@classmethod
|
||||
def _argument_split(cls, arguments):
|
||||
return re.split(cls._argument_split_re, arguments)
|
||||
|
||||
# Regex for interesting header lines.
|
||||
# Groups: 1=macro name, 2=type, 3=argument list (optional).
|
||||
_header_line_re = \
|
||||
|
|
@ -301,7 +242,7 @@ class Inputs:
|
|||
if m:
|
||||
self.add_test_case_line(m.group(1), m.group(2))
|
||||
|
||||
def gather_inputs(headers, test_suites, inputs_class=Inputs):
|
||||
def gather_inputs(headers, test_suites, inputs_class=InputsForTest):
|
||||
"""Read the list of inputs to test psa_constant_names with."""
|
||||
inputs = inputs_class()
|
||||
for header in headers:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue