mirror of
https://github.com/yuzu-emu/mbedtls.git
synced 2025-02-24 08:36:49 +00:00
Move PSAMacroEnumerator to macro_collector
It's useful for more than test_psa_constant_names. Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
parent
10ab267afb
commit
22fcf1b5f5
|
@ -16,8 +16,115 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import itertools
|
||||||
import re
|
import re
|
||||||
from typing import Dict, Set
|
from typing import Dict, Iterable, Iterator, List, Set
|
||||||
|
|
||||||
|
|
||||||
|
class PSAMacroEnumerator:
|
||||||
|
"""Information about constructors of various PSA Crypto types.
|
||||||
|
|
||||||
|
This includes macro names as well as information about their arguments
|
||||||
|
when applicable.
|
||||||
|
|
||||||
|
This class only provides ways to enumerate expressions that evaluate to
|
||||||
|
values of the covered types. Derived classes are expected to populate
|
||||||
|
the set of known constructors of each kind, as well as populate
|
||||||
|
`self.arguments_for` for arguments that are not of a kind that is
|
||||||
|
enumerated here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Set up an empty set of known constructor macros.
|
||||||
|
"""
|
||||||
|
self.statuses = set() #type: Set[str]
|
||||||
|
self.algorithms = set() #type: Set[str]
|
||||||
|
self.ecc_curves = set() #type: Set[str]
|
||||||
|
self.dh_groups = set() #type: Set[str]
|
||||||
|
self.key_types = set() #type: Set[str]
|
||||||
|
self.key_usage_flags = set() #type: Set[str]
|
||||||
|
self.hash_algorithms = set() #type: Set[str]
|
||||||
|
self.mac_algorithms = set() #type: Set[str]
|
||||||
|
self.ka_algorithms = set() #type: Set[str]
|
||||||
|
self.kdf_algorithms = set() #type: Set[str]
|
||||||
|
self.aead_algorithms = set() #type: Set[str]
|
||||||
|
# macro name -> list of argument names
|
||||||
|
self.argspecs = {} #type: Dict[str, List[str]]
|
||||||
|
# argument name -> list of values
|
||||||
|
self.arguments_for = {
|
||||||
|
'mac_length': [],
|
||||||
|
'min_mac_length': [],
|
||||||
|
'tag_length': [],
|
||||||
|
'min_tag_length': [],
|
||||||
|
} #type: Dict[str, List[str]]
|
||||||
|
|
||||||
|
def gather_arguments(self) -> None:
|
||||||
|
"""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: str, arguments: Iterable[str]) -> str:
|
||||||
|
"""Format a macro call with arguments.."""
|
||||||
|
return name + '(' + ', '.join(arguments) + ')'
|
||||||
|
|
||||||
|
_argument_split_re = re.compile(r' *, *')
|
||||||
|
@classmethod
|
||||||
|
def _argument_split(cls, arguments: str) -> List[str]:
|
||||||
|
return re.split(cls._argument_split_re, arguments)
|
||||||
|
|
||||||
|
def distribute_arguments(self, name: str) -> Iterator[str]:
|
||||||
|
"""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: Iterable[str]) -> Iterator[str]:
|
||||||
|
"""Generate expressions covering values constructed from the given names.
|
||||||
|
|
||||||
|
`names` can be any iterable collection of macro names.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* ``generate_expressions(['PSA_ALG_CMAC', 'PSA_ALG_HMAC'])``
|
||||||
|
generates ``'PSA_ALG_CMAC'`` as well as ``'PSA_ALG_HMAC(h)'`` for
|
||||||
|
every known hash algorithm ``h``.
|
||||||
|
* ``macros.generate_expressions(macros.key_types)`` generates all
|
||||||
|
key types.
|
||||||
|
"""
|
||||||
|
return itertools.chain(*map(self.distribute_arguments, names))
|
||||||
|
|
||||||
|
|
||||||
class PSAMacroCollector:
|
class PSAMacroCollector:
|
||||||
"""Collect PSA crypto macro definitions from C header files.
|
"""Collect PSA crypto macro definitions from C header files.
|
||||||
|
|
|
@ -24,119 +24,14 @@ or 1 (with a Python backtrace) if there was an operational error.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import itertools
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, Iterable, Iterator, List, Set
|
|
||||||
|
|
||||||
import scripts_path # pylint: disable=unused-import
|
import scripts_path # pylint: disable=unused-import
|
||||||
from mbedtls_dev import c_build_helper
|
from mbedtls_dev import c_build_helper
|
||||||
|
from mbedtls_dev import macro_collector
|
||||||
class PSAMacroEnumerator:
|
|
||||||
"""Information about constructors of various PSA Crypto types.
|
|
||||||
|
|
||||||
This includes macro names as well as information about their arguments
|
|
||||||
when applicable.
|
|
||||||
|
|
||||||
This class only provides ways to enumerate expressions that evaluate to
|
|
||||||
values of the covered types. Derived classes are expected to populate
|
|
||||||
the set of known constructors of each kind, as well as populate
|
|
||||||
`self.arguments_for` for arguments that are not of a kind that is
|
|
||||||
enumerated here.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
"""Set up an empty set of known constructor macros.
|
|
||||||
"""
|
|
||||||
self.statuses = set() #type: Set[str]
|
|
||||||
self.algorithms = set() #type: Set[str]
|
|
||||||
self.ecc_curves = set() #type: Set[str]
|
|
||||||
self.dh_groups = set() #type: Set[str]
|
|
||||||
self.key_types = set() #type: Set[str]
|
|
||||||
self.key_usage_flags = set() #type: Set[str]
|
|
||||||
self.hash_algorithms = set() #type: Set[str]
|
|
||||||
self.mac_algorithms = set() #type: Set[str]
|
|
||||||
self.ka_algorithms = set() #type: Set[str]
|
|
||||||
self.kdf_algorithms = set() #type: Set[str]
|
|
||||||
self.aead_algorithms = set() #type: Set[str]
|
|
||||||
# macro name -> list of argument names
|
|
||||||
self.argspecs = {} #type: Dict[str, List[str]]
|
|
||||||
# argument name -> list of values
|
|
||||||
self.arguments_for = {
|
|
||||||
'mac_length': [],
|
|
||||||
'min_mac_length': [],
|
|
||||||
'tag_length': [],
|
|
||||||
'min_tag_length': [],
|
|
||||||
} #type: Dict[str, List[str]]
|
|
||||||
|
|
||||||
def gather_arguments(self) -> None:
|
|
||||||
"""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: str, arguments: Iterable[str]) -> str:
|
|
||||||
"""Format a macro call with arguments.."""
|
|
||||||
return name + '(' + ', '.join(arguments) + ')'
|
|
||||||
|
|
||||||
_argument_split_re = re.compile(r' *, *')
|
|
||||||
@classmethod
|
|
||||||
def _argument_split(cls, arguments: str) -> List[str]:
|
|
||||||
return re.split(cls._argument_split_re, arguments)
|
|
||||||
|
|
||||||
def distribute_arguments(self, name: str) -> Iterator[str]:
|
|
||||||
"""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: Iterable[str]) -> Iterator[str]:
|
|
||||||
"""Generate expressions covering values constructed from the given names.
|
|
||||||
|
|
||||||
`names` can be any iterable collection of macro names.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
* ``generate_expressions(['PSA_ALG_CMAC', 'PSA_ALG_HMAC'])``
|
|
||||||
generates ``'PSA_ALG_CMAC'`` as well as ``'PSA_ALG_HMAC(h)'`` for
|
|
||||||
every known hash algorithm ``h``.
|
|
||||||
* ``macros.generate_expressions(macros.key_types)`` generates all
|
|
||||||
key types.
|
|
||||||
"""
|
|
||||||
return itertools.chain(*map(self.distribute_arguments, names))
|
|
||||||
|
|
||||||
class ReadFileLineException(Exception):
|
class ReadFileLineException(Exception):
|
||||||
def __init__(self, filename, line_number):
|
def __init__(self, filename, line_number):
|
||||||
|
@ -183,7 +78,7 @@ class read_file_lines:
|
||||||
raise ReadFileLineException(self.filename, self.line_number) \
|
raise ReadFileLineException(self.filename, self.line_number) \
|
||||||
from exc_value
|
from exc_value
|
||||||
|
|
||||||
class InputsForTest(PSAMacroEnumerator):
|
class InputsForTest(macro_collector.PSAMacroEnumerator):
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
"""Accumulate information about macros to test.
|
"""Accumulate information about macros to test.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue