Hook up PSAMacroCollector to PSAMacroEnumerator

Make it possible to enumerate the key types, algorithms, etc.
collected by PSAMacroCollector.

This commit ensures that all fields of PSAMacroEnumerator are filled
by code inspection. Testing of the result may reveal more work to be
done in later commits.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine 2021-03-10 01:25:50 +01:00
parent 22fcf1b5f5
commit 33c601cb73
2 changed files with 31 additions and 17 deletions

View File

@ -304,7 +304,7 @@ class CaseBuilder(macro_collector.PSAMacroCollector):
def _make_key_usage_code(self): def _make_key_usage_code(self):
return '\n'.join([self._make_bit_test('usage', bit) return '\n'.join([self._make_bit_test('usage', bit)
for bit in sorted(self.key_usages)]) for bit in sorted(self.key_usage_flags)])
def write_file(self, output_file): def write_file(self, output_file):
"""Generate the pretty-printer function code from the gathered """Generate the pretty-printer function code from the gathered

View File

@ -126,7 +126,7 @@ class PSAMacroEnumerator:
return itertools.chain(*map(self.distribute_arguments, names)) return itertools.chain(*map(self.distribute_arguments, names))
class PSAMacroCollector: class PSAMacroCollector(PSAMacroEnumerator):
"""Collect PSA crypto macro definitions from C header files. """Collect PSA crypto macro definitions from C header files.
""" """
@ -138,18 +138,11 @@ class PSAMacroCollector:
* include_intermediate: if true, include intermediate macros such as * include_intermediate: if true, include intermediate macros such as
PSA_XXX_BASE that do not designate semantic values. PSA_XXX_BASE that do not designate semantic values.
""" """
super().__init__()
self.include_intermediate = include_intermediate self.include_intermediate = include_intermediate
self.statuses = set() #type: Set[str]
self.key_types = set() #type: Set[str]
self.key_types_from_curve = {} #type: Dict[str, str] self.key_types_from_curve = {} #type: Dict[str, str]
self.key_types_from_group = {} #type: Dict[str, str] self.key_types_from_group = {} #type: Dict[str, str]
self.ecc_curves = set() #type: Set[str]
self.dh_groups = set() #type: Set[str]
self.algorithms = set() #type: Set[str]
self.hash_algorithms = set() #type: Set[str]
self.ka_algorithms = set() #type: Set[str]
self.algorithms_from_hash = {} #type: Dict[str, str] self.algorithms_from_hash = {} #type: Dict[str, str]
self.key_usages = set() #type: Set[str]
def is_internal_name(self, name: str) -> bool: def is_internal_name(self, name: str) -> bool:
"""Whether this is an internal macro. Internal macros will be skipped.""" """Whether this is an internal macro. Internal macros will be skipped."""
@ -160,6 +153,30 @@ class PSAMacroCollector:
return True return True
return name.endswith('_FLAG') or name.endswith('_MASK') return name.endswith('_FLAG') or name.endswith('_MASK')
def record_algorithm_subtype(self, name: str, expansion: str) -> None:
"""Record the subtype of an algorithm constructor.
Given a ``PSA_ALG_xxx`` macro name and its expansion, if the algorithm
is of a subtype that is tracked in its own set, add it to the relevant
set.
"""
# This code is very ad hoc and fragile. It should be replaced by
# something more robust.
if re.match(r'MAC(?:_|\Z)', name):
self.mac_algorithms.add(name)
elif re.match(r'KDF(?:_|\Z)', name):
self.kdf_algorithms.add(name)
elif re.search(r'0x020000[0-9A-Fa-f]{2}', expansion):
self.hash_algorithms.add(name)
elif re.search(r'0x03[0-9A-Fa-f]{6}', expansion):
self.mac_algorithms.add(name)
elif re.search(r'0x05[0-9A-Fa-f]{6}', expansion):
self.aead_algorithms.add(name)
elif re.search(r'0x09[0-9A-Fa-f]{2}0000', expansion):
self.ka_algorithms.add(name)
elif re.search(r'0x08[0-9A-Fa-f]{6}', expansion):
self.kdf_algorithms.add(name)
# "#define" followed by a macro name with either no parameters # "#define" followed by a macro name with either no parameters
# or a single parameter and a non-empty expansion. # or a single parameter and a non-empty expansion.
# Grab the macro name in group 1, the parameter name if any in group 2 # Grab the macro name in group 1, the parameter name if any in group 2
@ -180,6 +197,8 @@ class PSAMacroCollector:
return return
name, parameter, expansion = m.groups() name, parameter, expansion = m.groups()
expansion = re.sub(r'/\*.*?\*/|//.*', r' ', expansion) expansion = re.sub(r'/\*.*?\*/|//.*', r' ', expansion)
if parameter:
self.argspecs[name] = [parameter]
if re.match(self._deprecated_definition_re, expansion): if re.match(self._deprecated_definition_re, expansion):
# Skip deprecated values, which are assumed to be # Skip deprecated values, which are assumed to be
# backward compatibility aliases that share # backward compatibility aliases that share
@ -207,12 +226,7 @@ class PSAMacroCollector:
# Ad hoc skipping of duplicate names for some numerical values # Ad hoc skipping of duplicate names for some numerical values
return return
self.algorithms.add(name) self.algorithms.add(name)
# Ad hoc detection of hash algorithms self.record_algorithm_subtype(name, expansion)
if re.search(r'0x020000[0-9A-Fa-f]{2}', expansion):
self.hash_algorithms.add(name)
# Ad hoc detection of key agreement algorithms
if re.search(r'0x09[0-9A-Fa-f]{2}0000', expansion):
self.ka_algorithms.add(name)
elif name.startswith('PSA_ALG_') and parameter == 'hash_alg': elif name.startswith('PSA_ALG_') and parameter == 'hash_alg':
if name in ['PSA_ALG_DSA', 'PSA_ALG_ECDSA']: if name in ['PSA_ALG_DSA', 'PSA_ALG_ECDSA']:
# A naming irregularity # A naming irregularity
@ -221,7 +235,7 @@ class PSAMacroCollector:
tester = name[:8] + 'IS_' + name[8:] tester = name[:8] + 'IS_' + name[8:]
self.algorithms_from_hash[name] = tester self.algorithms_from_hash[name] = tester
elif name.startswith('PSA_KEY_USAGE_') and not parameter: elif name.startswith('PSA_KEY_USAGE_') and not parameter:
self.key_usages.add(name) self.key_usage_flags.add(name)
else: else:
# Other macro without parameter # Other macro without parameter
return return