xref: /aosp_15_r20/external/mbedtls/tests/scripts/generate_psa_tests.py (revision 62c56f9862f102b96d72393aff6076c951fb8148)
1*62c56f98SSadaf Ebrahimi#!/usr/bin/env python3
2*62c56f98SSadaf Ebrahimi"""Generate test data for PSA cryptographic mechanisms.
3*62c56f98SSadaf Ebrahimi
4*62c56f98SSadaf EbrahimiWith no arguments, generate all test data. With non-option arguments,
5*62c56f98SSadaf Ebrahimigenerate only the specified files.
6*62c56f98SSadaf Ebrahimi"""
7*62c56f98SSadaf Ebrahimi
8*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors
9*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
10*62c56f98SSadaf Ebrahimi
11*62c56f98SSadaf Ebrahimiimport enum
12*62c56f98SSadaf Ebrahimiimport re
13*62c56f98SSadaf Ebrahimiimport sys
14*62c56f98SSadaf Ebrahimifrom typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional
15*62c56f98SSadaf Ebrahimi
16*62c56f98SSadaf Ebrahimiimport scripts_path # pylint: disable=unused-import
17*62c56f98SSadaf Ebrahimifrom mbedtls_dev import crypto_data_tests
18*62c56f98SSadaf Ebrahimifrom mbedtls_dev import crypto_knowledge
19*62c56f98SSadaf Ebrahimifrom mbedtls_dev import macro_collector #pylint: disable=unused-import
20*62c56f98SSadaf Ebrahimifrom mbedtls_dev import psa_information
21*62c56f98SSadaf Ebrahimifrom mbedtls_dev import psa_storage
22*62c56f98SSadaf Ebrahimifrom mbedtls_dev import test_case
23*62c56f98SSadaf Ebrahimifrom mbedtls_dev import test_data_generation
24*62c56f98SSadaf Ebrahimi
25*62c56f98SSadaf Ebrahimi
26*62c56f98SSadaf Ebrahimi
27*62c56f98SSadaf Ebrahimidef test_case_for_key_type_not_supported(
28*62c56f98SSadaf Ebrahimi        verb: str, key_type: str, bits: int,
29*62c56f98SSadaf Ebrahimi        dependencies: List[str],
30*62c56f98SSadaf Ebrahimi        *args: str,
31*62c56f98SSadaf Ebrahimi        param_descr: str = ''
32*62c56f98SSadaf Ebrahimi) -> test_case.TestCase:
33*62c56f98SSadaf Ebrahimi    """Return one test case exercising a key creation method
34*62c56f98SSadaf Ebrahimi    for an unsupported key type or size.
35*62c56f98SSadaf Ebrahimi    """
36*62c56f98SSadaf Ebrahimi    psa_information.hack_dependencies_not_implemented(dependencies)
37*62c56f98SSadaf Ebrahimi    tc = test_case.TestCase()
38*62c56f98SSadaf Ebrahimi    short_key_type = crypto_knowledge.short_expression(key_type)
39*62c56f98SSadaf Ebrahimi    adverb = 'not' if dependencies else 'never'
40*62c56f98SSadaf Ebrahimi    if param_descr:
41*62c56f98SSadaf Ebrahimi        adverb = param_descr + ' ' + adverb
42*62c56f98SSadaf Ebrahimi    tc.set_description('PSA {} {} {}-bit {} supported'
43*62c56f98SSadaf Ebrahimi                       .format(verb, short_key_type, bits, adverb))
44*62c56f98SSadaf Ebrahimi    tc.set_dependencies(dependencies)
45*62c56f98SSadaf Ebrahimi    tc.set_function(verb + '_not_supported')
46*62c56f98SSadaf Ebrahimi    tc.set_arguments([key_type] + list(args))
47*62c56f98SSadaf Ebrahimi    return tc
48*62c56f98SSadaf Ebrahimi
49*62c56f98SSadaf Ebrahimiclass KeyTypeNotSupported:
50*62c56f98SSadaf Ebrahimi    """Generate test cases for when a key type is not supported."""
51*62c56f98SSadaf Ebrahimi
52*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information) -> None:
53*62c56f98SSadaf Ebrahimi        self.constructors = info.constructors
54*62c56f98SSadaf Ebrahimi
55*62c56f98SSadaf Ebrahimi    ALWAYS_SUPPORTED = frozenset([
56*62c56f98SSadaf Ebrahimi        'PSA_KEY_TYPE_DERIVE',
57*62c56f98SSadaf Ebrahimi        'PSA_KEY_TYPE_PASSWORD',
58*62c56f98SSadaf Ebrahimi        'PSA_KEY_TYPE_PASSWORD_HASH',
59*62c56f98SSadaf Ebrahimi        'PSA_KEY_TYPE_RAW_DATA',
60*62c56f98SSadaf Ebrahimi        'PSA_KEY_TYPE_HMAC'
61*62c56f98SSadaf Ebrahimi    ])
62*62c56f98SSadaf Ebrahimi    def test_cases_for_key_type_not_supported(
63*62c56f98SSadaf Ebrahimi            self,
64*62c56f98SSadaf Ebrahimi            kt: crypto_knowledge.KeyType,
65*62c56f98SSadaf Ebrahimi            param: Optional[int] = None,
66*62c56f98SSadaf Ebrahimi            param_descr: str = '',
67*62c56f98SSadaf Ebrahimi    ) -> Iterator[test_case.TestCase]:
68*62c56f98SSadaf Ebrahimi        """Return test cases exercising key creation when the given type is unsupported.
69*62c56f98SSadaf Ebrahimi
70*62c56f98SSadaf Ebrahimi        If param is present and not None, emit test cases conditioned on this
71*62c56f98SSadaf Ebrahimi        parameter not being supported. If it is absent or None, emit test cases
72*62c56f98SSadaf Ebrahimi        conditioned on the base type not being supported.
73*62c56f98SSadaf Ebrahimi        """
74*62c56f98SSadaf Ebrahimi        if kt.name in self.ALWAYS_SUPPORTED:
75*62c56f98SSadaf Ebrahimi            # Don't generate test cases for key types that are always supported.
76*62c56f98SSadaf Ebrahimi            # They would be skipped in all configurations, which is noise.
77*62c56f98SSadaf Ebrahimi            return
78*62c56f98SSadaf Ebrahimi        import_dependencies = [('!' if param is None else '') +
79*62c56f98SSadaf Ebrahimi                               psa_information.psa_want_symbol(kt.name)]
80*62c56f98SSadaf Ebrahimi        if kt.params is not None:
81*62c56f98SSadaf Ebrahimi            import_dependencies += [('!' if param == i else '') +
82*62c56f98SSadaf Ebrahimi                                    psa_information.psa_want_symbol(sym)
83*62c56f98SSadaf Ebrahimi                                    for i, sym in enumerate(kt.params)]
84*62c56f98SSadaf Ebrahimi        if kt.name.endswith('_PUBLIC_KEY'):
85*62c56f98SSadaf Ebrahimi            generate_dependencies = []
86*62c56f98SSadaf Ebrahimi        else:
87*62c56f98SSadaf Ebrahimi            generate_dependencies = \
88*62c56f98SSadaf Ebrahimi                psa_information.fix_key_pair_dependencies(import_dependencies, 'GENERATE')
89*62c56f98SSadaf Ebrahimi            import_dependencies = \
90*62c56f98SSadaf Ebrahimi                psa_information.fix_key_pair_dependencies(import_dependencies, 'BASIC')
91*62c56f98SSadaf Ebrahimi        for bits in kt.sizes_to_test():
92*62c56f98SSadaf Ebrahimi            yield test_case_for_key_type_not_supported(
93*62c56f98SSadaf Ebrahimi                'import', kt.expression, bits,
94*62c56f98SSadaf Ebrahimi                psa_information.finish_family_dependencies(import_dependencies, bits),
95*62c56f98SSadaf Ebrahimi                test_case.hex_string(kt.key_material(bits)),
96*62c56f98SSadaf Ebrahimi                param_descr=param_descr,
97*62c56f98SSadaf Ebrahimi            )
98*62c56f98SSadaf Ebrahimi            if not generate_dependencies and param is not None:
99*62c56f98SSadaf Ebrahimi                # If generation is impossible for this key type, rather than
100*62c56f98SSadaf Ebrahimi                # supported or not depending on implementation capabilities,
101*62c56f98SSadaf Ebrahimi                # only generate the test case once.
102*62c56f98SSadaf Ebrahimi                continue
103*62c56f98SSadaf Ebrahimi                # For public key we expect that key generation fails with
104*62c56f98SSadaf Ebrahimi                # INVALID_ARGUMENT. It is handled by KeyGenerate class.
105*62c56f98SSadaf Ebrahimi            if not kt.is_public():
106*62c56f98SSadaf Ebrahimi                yield test_case_for_key_type_not_supported(
107*62c56f98SSadaf Ebrahimi                    'generate', kt.expression, bits,
108*62c56f98SSadaf Ebrahimi                    psa_information.finish_family_dependencies(generate_dependencies, bits),
109*62c56f98SSadaf Ebrahimi                    str(bits),
110*62c56f98SSadaf Ebrahimi                    param_descr=param_descr,
111*62c56f98SSadaf Ebrahimi                )
112*62c56f98SSadaf Ebrahimi            # To be added: derive
113*62c56f98SSadaf Ebrahimi
114*62c56f98SSadaf Ebrahimi    ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
115*62c56f98SSadaf Ebrahimi                     'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
116*62c56f98SSadaf Ebrahimi    DH_KEY_TYPES = ('PSA_KEY_TYPE_DH_KEY_PAIR',
117*62c56f98SSadaf Ebrahimi                    'PSA_KEY_TYPE_DH_PUBLIC_KEY')
118*62c56f98SSadaf Ebrahimi
119*62c56f98SSadaf Ebrahimi    def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
120*62c56f98SSadaf Ebrahimi        """Generate test cases that exercise the creation of keys of unsupported types."""
121*62c56f98SSadaf Ebrahimi        for key_type in sorted(self.constructors.key_types):
122*62c56f98SSadaf Ebrahimi            if key_type in self.ECC_KEY_TYPES:
123*62c56f98SSadaf Ebrahimi                continue
124*62c56f98SSadaf Ebrahimi            if key_type in self.DH_KEY_TYPES:
125*62c56f98SSadaf Ebrahimi                continue
126*62c56f98SSadaf Ebrahimi            kt = crypto_knowledge.KeyType(key_type)
127*62c56f98SSadaf Ebrahimi            yield from self.test_cases_for_key_type_not_supported(kt)
128*62c56f98SSadaf Ebrahimi        for curve_family in sorted(self.constructors.ecc_curves):
129*62c56f98SSadaf Ebrahimi            for constr in self.ECC_KEY_TYPES:
130*62c56f98SSadaf Ebrahimi                kt = crypto_knowledge.KeyType(constr, [curve_family])
131*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_not_supported(
132*62c56f98SSadaf Ebrahimi                    kt, param_descr='type')
133*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_not_supported(
134*62c56f98SSadaf Ebrahimi                    kt, 0, param_descr='curve')
135*62c56f98SSadaf Ebrahimi        for dh_family in sorted(self.constructors.dh_groups):
136*62c56f98SSadaf Ebrahimi            for constr in self.DH_KEY_TYPES:
137*62c56f98SSadaf Ebrahimi                kt = crypto_knowledge.KeyType(constr, [dh_family])
138*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_not_supported(
139*62c56f98SSadaf Ebrahimi                    kt, param_descr='type')
140*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_not_supported(
141*62c56f98SSadaf Ebrahimi                    kt, 0, param_descr='group')
142*62c56f98SSadaf Ebrahimi
143*62c56f98SSadaf Ebrahimidef test_case_for_key_generation(
144*62c56f98SSadaf Ebrahimi        key_type: str, bits: int,
145*62c56f98SSadaf Ebrahimi        dependencies: List[str],
146*62c56f98SSadaf Ebrahimi        *args: str,
147*62c56f98SSadaf Ebrahimi        result: str = ''
148*62c56f98SSadaf Ebrahimi) -> test_case.TestCase:
149*62c56f98SSadaf Ebrahimi    """Return one test case exercising a key generation.
150*62c56f98SSadaf Ebrahimi    """
151*62c56f98SSadaf Ebrahimi    psa_information.hack_dependencies_not_implemented(dependencies)
152*62c56f98SSadaf Ebrahimi    tc = test_case.TestCase()
153*62c56f98SSadaf Ebrahimi    short_key_type = crypto_knowledge.short_expression(key_type)
154*62c56f98SSadaf Ebrahimi    tc.set_description('PSA {} {}-bit'
155*62c56f98SSadaf Ebrahimi                       .format(short_key_type, bits))
156*62c56f98SSadaf Ebrahimi    tc.set_dependencies(dependencies)
157*62c56f98SSadaf Ebrahimi    tc.set_function('generate_key')
158*62c56f98SSadaf Ebrahimi    tc.set_arguments([key_type] + list(args) + [result])
159*62c56f98SSadaf Ebrahimi
160*62c56f98SSadaf Ebrahimi    return tc
161*62c56f98SSadaf Ebrahimi
162*62c56f98SSadaf Ebrahimiclass KeyGenerate:
163*62c56f98SSadaf Ebrahimi    """Generate positive and negative (invalid argument) test cases for key generation."""
164*62c56f98SSadaf Ebrahimi
165*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information) -> None:
166*62c56f98SSadaf Ebrahimi        self.constructors = info.constructors
167*62c56f98SSadaf Ebrahimi
168*62c56f98SSadaf Ebrahimi    ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
169*62c56f98SSadaf Ebrahimi                     'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
170*62c56f98SSadaf Ebrahimi    DH_KEY_TYPES = ('PSA_KEY_TYPE_DH_KEY_PAIR',
171*62c56f98SSadaf Ebrahimi                    'PSA_KEY_TYPE_DH_PUBLIC_KEY')
172*62c56f98SSadaf Ebrahimi
173*62c56f98SSadaf Ebrahimi    @staticmethod
174*62c56f98SSadaf Ebrahimi    def test_cases_for_key_type_key_generation(
175*62c56f98SSadaf Ebrahimi            kt: crypto_knowledge.KeyType
176*62c56f98SSadaf Ebrahimi    ) -> Iterator[test_case.TestCase]:
177*62c56f98SSadaf Ebrahimi        """Return test cases exercising key generation.
178*62c56f98SSadaf Ebrahimi
179*62c56f98SSadaf Ebrahimi        All key types can be generated except for public keys. For public key
180*62c56f98SSadaf Ebrahimi        PSA_ERROR_INVALID_ARGUMENT status is expected.
181*62c56f98SSadaf Ebrahimi        """
182*62c56f98SSadaf Ebrahimi        result = 'PSA_SUCCESS'
183*62c56f98SSadaf Ebrahimi
184*62c56f98SSadaf Ebrahimi        import_dependencies = [psa_information.psa_want_symbol(kt.name)]
185*62c56f98SSadaf Ebrahimi        if kt.params is not None:
186*62c56f98SSadaf Ebrahimi            import_dependencies += [psa_information.psa_want_symbol(sym)
187*62c56f98SSadaf Ebrahimi                                    for i, sym in enumerate(kt.params)]
188*62c56f98SSadaf Ebrahimi        if kt.name.endswith('_PUBLIC_KEY'):
189*62c56f98SSadaf Ebrahimi            # The library checks whether the key type is a public key generically,
190*62c56f98SSadaf Ebrahimi            # before it reaches a point where it needs support for the specific key
191*62c56f98SSadaf Ebrahimi            # type, so it returns INVALID_ARGUMENT for unsupported public key types.
192*62c56f98SSadaf Ebrahimi            generate_dependencies = []
193*62c56f98SSadaf Ebrahimi            result = 'PSA_ERROR_INVALID_ARGUMENT'
194*62c56f98SSadaf Ebrahimi        else:
195*62c56f98SSadaf Ebrahimi            generate_dependencies = \
196*62c56f98SSadaf Ebrahimi                psa_information.fix_key_pair_dependencies(import_dependencies, 'GENERATE')
197*62c56f98SSadaf Ebrahimi        for bits in kt.sizes_to_test():
198*62c56f98SSadaf Ebrahimi            if kt.name == 'PSA_KEY_TYPE_RSA_KEY_PAIR':
199*62c56f98SSadaf Ebrahimi                size_dependency = "PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS <= " +  str(bits)
200*62c56f98SSadaf Ebrahimi                test_dependencies = generate_dependencies + [size_dependency]
201*62c56f98SSadaf Ebrahimi            else:
202*62c56f98SSadaf Ebrahimi                test_dependencies = generate_dependencies
203*62c56f98SSadaf Ebrahimi            yield test_case_for_key_generation(
204*62c56f98SSadaf Ebrahimi                kt.expression, bits,
205*62c56f98SSadaf Ebrahimi                psa_information.finish_family_dependencies(test_dependencies, bits),
206*62c56f98SSadaf Ebrahimi                str(bits),
207*62c56f98SSadaf Ebrahimi                result
208*62c56f98SSadaf Ebrahimi            )
209*62c56f98SSadaf Ebrahimi
210*62c56f98SSadaf Ebrahimi    def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
211*62c56f98SSadaf Ebrahimi        """Generate test cases that exercise the generation of keys."""
212*62c56f98SSadaf Ebrahimi        for key_type in sorted(self.constructors.key_types):
213*62c56f98SSadaf Ebrahimi            if key_type in self.ECC_KEY_TYPES:
214*62c56f98SSadaf Ebrahimi                continue
215*62c56f98SSadaf Ebrahimi            if key_type in self.DH_KEY_TYPES:
216*62c56f98SSadaf Ebrahimi                continue
217*62c56f98SSadaf Ebrahimi            kt = crypto_knowledge.KeyType(key_type)
218*62c56f98SSadaf Ebrahimi            yield from self.test_cases_for_key_type_key_generation(kt)
219*62c56f98SSadaf Ebrahimi        for curve_family in sorted(self.constructors.ecc_curves):
220*62c56f98SSadaf Ebrahimi            for constr in self.ECC_KEY_TYPES:
221*62c56f98SSadaf Ebrahimi                kt = crypto_knowledge.KeyType(constr, [curve_family])
222*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_key_generation(kt)
223*62c56f98SSadaf Ebrahimi        for dh_family in sorted(self.constructors.dh_groups):
224*62c56f98SSadaf Ebrahimi            for constr in self.DH_KEY_TYPES:
225*62c56f98SSadaf Ebrahimi                kt = crypto_knowledge.KeyType(constr, [dh_family])
226*62c56f98SSadaf Ebrahimi                yield from self.test_cases_for_key_type_key_generation(kt)
227*62c56f98SSadaf Ebrahimi
228*62c56f98SSadaf Ebrahimiclass OpFail:
229*62c56f98SSadaf Ebrahimi    """Generate test cases for operations that must fail."""
230*62c56f98SSadaf Ebrahimi    #pylint: disable=too-few-public-methods
231*62c56f98SSadaf Ebrahimi
232*62c56f98SSadaf Ebrahimi    class Reason(enum.Enum):
233*62c56f98SSadaf Ebrahimi        NOT_SUPPORTED = 0
234*62c56f98SSadaf Ebrahimi        INVALID = 1
235*62c56f98SSadaf Ebrahimi        INCOMPATIBLE = 2
236*62c56f98SSadaf Ebrahimi        PUBLIC = 3
237*62c56f98SSadaf Ebrahimi
238*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information) -> None:
239*62c56f98SSadaf Ebrahimi        self.constructors = info.constructors
240*62c56f98SSadaf Ebrahimi        key_type_expressions = self.constructors.generate_expressions(
241*62c56f98SSadaf Ebrahimi            sorted(self.constructors.key_types)
242*62c56f98SSadaf Ebrahimi        )
243*62c56f98SSadaf Ebrahimi        self.key_types = [crypto_knowledge.KeyType(kt_expr)
244*62c56f98SSadaf Ebrahimi                          for kt_expr in key_type_expressions]
245*62c56f98SSadaf Ebrahimi
246*62c56f98SSadaf Ebrahimi    def make_test_case(
247*62c56f98SSadaf Ebrahimi            self,
248*62c56f98SSadaf Ebrahimi            alg: crypto_knowledge.Algorithm,
249*62c56f98SSadaf Ebrahimi            category: crypto_knowledge.AlgorithmCategory,
250*62c56f98SSadaf Ebrahimi            reason: 'Reason',
251*62c56f98SSadaf Ebrahimi            kt: Optional[crypto_knowledge.KeyType] = None,
252*62c56f98SSadaf Ebrahimi            not_deps: FrozenSet[str] = frozenset(),
253*62c56f98SSadaf Ebrahimi    ) -> test_case.TestCase:
254*62c56f98SSadaf Ebrahimi        """Construct a failure test case for a one-key or keyless operation."""
255*62c56f98SSadaf Ebrahimi        #pylint: disable=too-many-arguments,too-many-locals
256*62c56f98SSadaf Ebrahimi        tc = test_case.TestCase()
257*62c56f98SSadaf Ebrahimi        pretty_alg = alg.short_expression()
258*62c56f98SSadaf Ebrahimi        if reason == self.Reason.NOT_SUPPORTED:
259*62c56f98SSadaf Ebrahimi            short_deps = [re.sub(r'PSA_WANT_ALG_', r'', dep)
260*62c56f98SSadaf Ebrahimi                          for dep in not_deps]
261*62c56f98SSadaf Ebrahimi            pretty_reason = '!' + '&'.join(sorted(short_deps))
262*62c56f98SSadaf Ebrahimi        else:
263*62c56f98SSadaf Ebrahimi            pretty_reason = reason.name.lower()
264*62c56f98SSadaf Ebrahimi        if kt:
265*62c56f98SSadaf Ebrahimi            key_type = kt.expression
266*62c56f98SSadaf Ebrahimi            pretty_type = kt.short_expression()
267*62c56f98SSadaf Ebrahimi        else:
268*62c56f98SSadaf Ebrahimi            key_type = ''
269*62c56f98SSadaf Ebrahimi            pretty_type = ''
270*62c56f98SSadaf Ebrahimi        tc.set_description('PSA {} {}: {}{}'
271*62c56f98SSadaf Ebrahimi                           .format(category.name.lower(),
272*62c56f98SSadaf Ebrahimi                                   pretty_alg,
273*62c56f98SSadaf Ebrahimi                                   pretty_reason,
274*62c56f98SSadaf Ebrahimi                                   ' with ' + pretty_type if pretty_type else ''))
275*62c56f98SSadaf Ebrahimi        dependencies = psa_information.automatic_dependencies(alg.base_expression, key_type)
276*62c56f98SSadaf Ebrahimi        dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC')
277*62c56f98SSadaf Ebrahimi        for i, dep in enumerate(dependencies):
278*62c56f98SSadaf Ebrahimi            if dep in not_deps:
279*62c56f98SSadaf Ebrahimi                dependencies[i] = '!' + dep
280*62c56f98SSadaf Ebrahimi        tc.set_dependencies(dependencies)
281*62c56f98SSadaf Ebrahimi        tc.set_function(category.name.lower() + '_fail')
282*62c56f98SSadaf Ebrahimi        arguments = [] # type: List[str]
283*62c56f98SSadaf Ebrahimi        if kt:
284*62c56f98SSadaf Ebrahimi            key_material = kt.key_material(kt.sizes_to_test()[0])
285*62c56f98SSadaf Ebrahimi            arguments += [key_type, test_case.hex_string(key_material)]
286*62c56f98SSadaf Ebrahimi        arguments.append(alg.expression)
287*62c56f98SSadaf Ebrahimi        if category.is_asymmetric():
288*62c56f98SSadaf Ebrahimi            arguments.append('1' if reason == self.Reason.PUBLIC else '0')
289*62c56f98SSadaf Ebrahimi        error = ('NOT_SUPPORTED' if reason == self.Reason.NOT_SUPPORTED else
290*62c56f98SSadaf Ebrahimi                 'INVALID_ARGUMENT')
291*62c56f98SSadaf Ebrahimi        arguments.append('PSA_ERROR_' + error)
292*62c56f98SSadaf Ebrahimi        tc.set_arguments(arguments)
293*62c56f98SSadaf Ebrahimi        return tc
294*62c56f98SSadaf Ebrahimi
295*62c56f98SSadaf Ebrahimi    def no_key_test_cases(
296*62c56f98SSadaf Ebrahimi            self,
297*62c56f98SSadaf Ebrahimi            alg: crypto_knowledge.Algorithm,
298*62c56f98SSadaf Ebrahimi            category: crypto_knowledge.AlgorithmCategory,
299*62c56f98SSadaf Ebrahimi    ) -> Iterator[test_case.TestCase]:
300*62c56f98SSadaf Ebrahimi        """Generate failure test cases for keyless operations with the specified algorithm."""
301*62c56f98SSadaf Ebrahimi        if alg.can_do(category):
302*62c56f98SSadaf Ebrahimi            # Compatible operation, unsupported algorithm
303*62c56f98SSadaf Ebrahimi            for dep in psa_information.automatic_dependencies(alg.base_expression):
304*62c56f98SSadaf Ebrahimi                yield self.make_test_case(alg, category,
305*62c56f98SSadaf Ebrahimi                                          self.Reason.NOT_SUPPORTED,
306*62c56f98SSadaf Ebrahimi                                          not_deps=frozenset([dep]))
307*62c56f98SSadaf Ebrahimi        else:
308*62c56f98SSadaf Ebrahimi            # Incompatible operation, supported algorithm
309*62c56f98SSadaf Ebrahimi            yield self.make_test_case(alg, category, self.Reason.INVALID)
310*62c56f98SSadaf Ebrahimi
311*62c56f98SSadaf Ebrahimi    def one_key_test_cases(
312*62c56f98SSadaf Ebrahimi            self,
313*62c56f98SSadaf Ebrahimi            alg: crypto_knowledge.Algorithm,
314*62c56f98SSadaf Ebrahimi            category: crypto_knowledge.AlgorithmCategory,
315*62c56f98SSadaf Ebrahimi    ) -> Iterator[test_case.TestCase]:
316*62c56f98SSadaf Ebrahimi        """Generate failure test cases for one-key operations with the specified algorithm."""
317*62c56f98SSadaf Ebrahimi        for kt in self.key_types:
318*62c56f98SSadaf Ebrahimi            key_is_compatible = kt.can_do(alg)
319*62c56f98SSadaf Ebrahimi            if key_is_compatible and alg.can_do(category):
320*62c56f98SSadaf Ebrahimi                # Compatible key and operation, unsupported algorithm
321*62c56f98SSadaf Ebrahimi                for dep in psa_information.automatic_dependencies(alg.base_expression):
322*62c56f98SSadaf Ebrahimi                    yield self.make_test_case(alg, category,
323*62c56f98SSadaf Ebrahimi                                              self.Reason.NOT_SUPPORTED,
324*62c56f98SSadaf Ebrahimi                                              kt=kt, not_deps=frozenset([dep]))
325*62c56f98SSadaf Ebrahimi                # Public key for a private-key operation
326*62c56f98SSadaf Ebrahimi                if category.is_asymmetric() and kt.is_public():
327*62c56f98SSadaf Ebrahimi                    yield self.make_test_case(alg, category,
328*62c56f98SSadaf Ebrahimi                                              self.Reason.PUBLIC,
329*62c56f98SSadaf Ebrahimi                                              kt=kt)
330*62c56f98SSadaf Ebrahimi            elif key_is_compatible:
331*62c56f98SSadaf Ebrahimi                # Compatible key, incompatible operation, supported algorithm
332*62c56f98SSadaf Ebrahimi                yield self.make_test_case(alg, category,
333*62c56f98SSadaf Ebrahimi                                          self.Reason.INVALID,
334*62c56f98SSadaf Ebrahimi                                          kt=kt)
335*62c56f98SSadaf Ebrahimi            elif alg.can_do(category):
336*62c56f98SSadaf Ebrahimi                # Incompatible key, compatible operation, supported algorithm
337*62c56f98SSadaf Ebrahimi                yield self.make_test_case(alg, category,
338*62c56f98SSadaf Ebrahimi                                          self.Reason.INCOMPATIBLE,
339*62c56f98SSadaf Ebrahimi                                          kt=kt)
340*62c56f98SSadaf Ebrahimi            else:
341*62c56f98SSadaf Ebrahimi                # Incompatible key and operation. Don't test cases where
342*62c56f98SSadaf Ebrahimi                # multiple things are wrong, to keep the number of test
343*62c56f98SSadaf Ebrahimi                # cases reasonable.
344*62c56f98SSadaf Ebrahimi                pass
345*62c56f98SSadaf Ebrahimi
346*62c56f98SSadaf Ebrahimi    def test_cases_for_algorithm(
347*62c56f98SSadaf Ebrahimi            self,
348*62c56f98SSadaf Ebrahimi            alg: crypto_knowledge.Algorithm,
349*62c56f98SSadaf Ebrahimi    ) -> Iterator[test_case.TestCase]:
350*62c56f98SSadaf Ebrahimi        """Generate operation failure test cases for the specified algorithm."""
351*62c56f98SSadaf Ebrahimi        for category in crypto_knowledge.AlgorithmCategory:
352*62c56f98SSadaf Ebrahimi            if category == crypto_knowledge.AlgorithmCategory.PAKE:
353*62c56f98SSadaf Ebrahimi                # PAKE operations are not implemented yet
354*62c56f98SSadaf Ebrahimi                pass
355*62c56f98SSadaf Ebrahimi            elif category.requires_key():
356*62c56f98SSadaf Ebrahimi                yield from self.one_key_test_cases(alg, category)
357*62c56f98SSadaf Ebrahimi            else:
358*62c56f98SSadaf Ebrahimi                yield from self.no_key_test_cases(alg, category)
359*62c56f98SSadaf Ebrahimi
360*62c56f98SSadaf Ebrahimi    def all_test_cases(self) -> Iterator[test_case.TestCase]:
361*62c56f98SSadaf Ebrahimi        """Generate all test cases for operations that must fail."""
362*62c56f98SSadaf Ebrahimi        algorithms = sorted(self.constructors.algorithms)
363*62c56f98SSadaf Ebrahimi        for expr in self.constructors.generate_expressions(algorithms):
364*62c56f98SSadaf Ebrahimi            alg = crypto_knowledge.Algorithm(expr)
365*62c56f98SSadaf Ebrahimi            yield from self.test_cases_for_algorithm(alg)
366*62c56f98SSadaf Ebrahimi
367*62c56f98SSadaf Ebrahimi
368*62c56f98SSadaf Ebrahimiclass StorageKey(psa_storage.Key):
369*62c56f98SSadaf Ebrahimi    """Representation of a key for storage format testing."""
370*62c56f98SSadaf Ebrahimi
371*62c56f98SSadaf Ebrahimi    IMPLICIT_USAGE_FLAGS = {
372*62c56f98SSadaf Ebrahimi        'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
373*62c56f98SSadaf Ebrahimi        'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
374*62c56f98SSadaf Ebrahimi    } #type: Dict[str, str]
375*62c56f98SSadaf Ebrahimi    """Mapping of usage flags to the flags that they imply."""
376*62c56f98SSadaf Ebrahimi
377*62c56f98SSadaf Ebrahimi    def __init__(
378*62c56f98SSadaf Ebrahimi            self,
379*62c56f98SSadaf Ebrahimi            usage: Iterable[str],
380*62c56f98SSadaf Ebrahimi            without_implicit_usage: Optional[bool] = False,
381*62c56f98SSadaf Ebrahimi            **kwargs
382*62c56f98SSadaf Ebrahimi    ) -> None:
383*62c56f98SSadaf Ebrahimi        """Prepare to generate a key.
384*62c56f98SSadaf Ebrahimi
385*62c56f98SSadaf Ebrahimi        * `usage`                 : The usage flags used for the key.
386*62c56f98SSadaf Ebrahimi        * `without_implicit_usage`: Flag to define to apply the usage extension
387*62c56f98SSadaf Ebrahimi        """
388*62c56f98SSadaf Ebrahimi        usage_flags = set(usage)
389*62c56f98SSadaf Ebrahimi        if not without_implicit_usage:
390*62c56f98SSadaf Ebrahimi            for flag in sorted(usage_flags):
391*62c56f98SSadaf Ebrahimi                if flag in self.IMPLICIT_USAGE_FLAGS:
392*62c56f98SSadaf Ebrahimi                    usage_flags.add(self.IMPLICIT_USAGE_FLAGS[flag])
393*62c56f98SSadaf Ebrahimi        if usage_flags:
394*62c56f98SSadaf Ebrahimi            usage_expression = ' | '.join(sorted(usage_flags))
395*62c56f98SSadaf Ebrahimi        else:
396*62c56f98SSadaf Ebrahimi            usage_expression = '0'
397*62c56f98SSadaf Ebrahimi        super().__init__(usage=usage_expression, **kwargs)
398*62c56f98SSadaf Ebrahimi
399*62c56f98SSadaf Ebrahimiclass StorageTestData(StorageKey):
400*62c56f98SSadaf Ebrahimi    """Representation of test case data for storage format testing."""
401*62c56f98SSadaf Ebrahimi
402*62c56f98SSadaf Ebrahimi    def __init__(
403*62c56f98SSadaf Ebrahimi            self,
404*62c56f98SSadaf Ebrahimi            description: str,
405*62c56f98SSadaf Ebrahimi            expected_usage: Optional[List[str]] = None,
406*62c56f98SSadaf Ebrahimi            **kwargs
407*62c56f98SSadaf Ebrahimi    ) -> None:
408*62c56f98SSadaf Ebrahimi        """Prepare to generate test data
409*62c56f98SSadaf Ebrahimi
410*62c56f98SSadaf Ebrahimi        * `description`   : used for the test case names
411*62c56f98SSadaf Ebrahimi        * `expected_usage`: the usage flags generated as the expected usage flags
412*62c56f98SSadaf Ebrahimi                            in the test cases. CAn differ from the usage flags
413*62c56f98SSadaf Ebrahimi                            stored in the keys because of the usage flags extension.
414*62c56f98SSadaf Ebrahimi        """
415*62c56f98SSadaf Ebrahimi        super().__init__(**kwargs)
416*62c56f98SSadaf Ebrahimi        self.description = description #type: str
417*62c56f98SSadaf Ebrahimi        if expected_usage is None:
418*62c56f98SSadaf Ebrahimi            self.expected_usage = self.usage #type: psa_storage.Expr
419*62c56f98SSadaf Ebrahimi        elif expected_usage:
420*62c56f98SSadaf Ebrahimi            self.expected_usage = psa_storage.Expr(' | '.join(expected_usage))
421*62c56f98SSadaf Ebrahimi        else:
422*62c56f98SSadaf Ebrahimi            self.expected_usage = psa_storage.Expr(0)
423*62c56f98SSadaf Ebrahimi
424*62c56f98SSadaf Ebrahimiclass StorageFormat:
425*62c56f98SSadaf Ebrahimi    """Storage format stability test cases."""
426*62c56f98SSadaf Ebrahimi
427*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information, version: int, forward: bool) -> None:
428*62c56f98SSadaf Ebrahimi        """Prepare to generate test cases for storage format stability.
429*62c56f98SSadaf Ebrahimi
430*62c56f98SSadaf Ebrahimi        * `info`: information about the API. See the `Information` class.
431*62c56f98SSadaf Ebrahimi        * `version`: the storage format version to generate test cases for.
432*62c56f98SSadaf Ebrahimi        * `forward`: if true, generate forward compatibility test cases which
433*62c56f98SSadaf Ebrahimi          save a key and check that its representation is as intended. Otherwise
434*62c56f98SSadaf Ebrahimi          generate backward compatibility test cases which inject a key
435*62c56f98SSadaf Ebrahimi          representation and check that it can be read and used.
436*62c56f98SSadaf Ebrahimi        """
437*62c56f98SSadaf Ebrahimi        self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
438*62c56f98SSadaf Ebrahimi        self.version = version #type: int
439*62c56f98SSadaf Ebrahimi        self.forward = forward #type: bool
440*62c56f98SSadaf Ebrahimi
441*62c56f98SSadaf Ebrahimi    RSA_OAEP_RE = re.compile(r'PSA_ALG_RSA_OAEP\((.*)\)\Z')
442*62c56f98SSadaf Ebrahimi    BRAINPOOL_RE = re.compile(r'PSA_KEY_TYPE_\w+\(PSA_ECC_FAMILY_BRAINPOOL_\w+\)\Z')
443*62c56f98SSadaf Ebrahimi    @classmethod
444*62c56f98SSadaf Ebrahimi    def exercise_key_with_algorithm(
445*62c56f98SSadaf Ebrahimi            cls,
446*62c56f98SSadaf Ebrahimi            key_type: psa_storage.Expr, bits: int,
447*62c56f98SSadaf Ebrahimi            alg: psa_storage.Expr
448*62c56f98SSadaf Ebrahimi    ) -> bool:
449*62c56f98SSadaf Ebrahimi        """Whether to exercise the given key with the given algorithm.
450*62c56f98SSadaf Ebrahimi
451*62c56f98SSadaf Ebrahimi        Normally only the type and algorithm matter for compatibility, and
452*62c56f98SSadaf Ebrahimi        this is handled in crypto_knowledge.KeyType.can_do(). This function
453*62c56f98SSadaf Ebrahimi        exists to detect exceptional cases. Exceptional cases detected here
454*62c56f98SSadaf Ebrahimi        are not tested in OpFail and should therefore have manually written
455*62c56f98SSadaf Ebrahimi        test cases.
456*62c56f98SSadaf Ebrahimi        """
457*62c56f98SSadaf Ebrahimi        # Some test keys have the RAW_DATA type and attributes that don't
458*62c56f98SSadaf Ebrahimi        # necessarily make sense. We do this to validate numerical
459*62c56f98SSadaf Ebrahimi        # encodings of the attributes.
460*62c56f98SSadaf Ebrahimi        # Raw data keys have no useful exercise anyway so there is no
461*62c56f98SSadaf Ebrahimi        # loss of test coverage.
462*62c56f98SSadaf Ebrahimi        if key_type.string == 'PSA_KEY_TYPE_RAW_DATA':
463*62c56f98SSadaf Ebrahimi            return False
464*62c56f98SSadaf Ebrahimi        # OAEP requires room for two hashes plus wrapping
465*62c56f98SSadaf Ebrahimi        m = cls.RSA_OAEP_RE.match(alg.string)
466*62c56f98SSadaf Ebrahimi        if m:
467*62c56f98SSadaf Ebrahimi            hash_alg = m.group(1)
468*62c56f98SSadaf Ebrahimi            hash_length = crypto_knowledge.Algorithm.hash_length(hash_alg)
469*62c56f98SSadaf Ebrahimi            key_length = (bits + 7) // 8
470*62c56f98SSadaf Ebrahimi            # Leave enough room for at least one byte of plaintext
471*62c56f98SSadaf Ebrahimi            return key_length > 2 * hash_length + 2
472*62c56f98SSadaf Ebrahimi        # There's nothing wrong with ECC keys on Brainpool curves,
473*62c56f98SSadaf Ebrahimi        # but operations with them are very slow. So we only exercise them
474*62c56f98SSadaf Ebrahimi        # with a single algorithm, not with all possible hashes. We do
475*62c56f98SSadaf Ebrahimi        # exercise other curves with all algorithms so test coverage is
476*62c56f98SSadaf Ebrahimi        # perfectly adequate like this.
477*62c56f98SSadaf Ebrahimi        m = cls.BRAINPOOL_RE.match(key_type.string)
478*62c56f98SSadaf Ebrahimi        if m and alg.string != 'PSA_ALG_ECDSA_ANY':
479*62c56f98SSadaf Ebrahimi            return False
480*62c56f98SSadaf Ebrahimi        return True
481*62c56f98SSadaf Ebrahimi
482*62c56f98SSadaf Ebrahimi    def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
483*62c56f98SSadaf Ebrahimi        """Construct a storage format test case for the given key.
484*62c56f98SSadaf Ebrahimi
485*62c56f98SSadaf Ebrahimi        If ``forward`` is true, generate a forward compatibility test case:
486*62c56f98SSadaf Ebrahimi        create a key and validate that it has the expected representation.
487*62c56f98SSadaf Ebrahimi        Otherwise generate a backward compatibility test case: inject the
488*62c56f98SSadaf Ebrahimi        key representation into storage and validate that it can be read
489*62c56f98SSadaf Ebrahimi        correctly.
490*62c56f98SSadaf Ebrahimi        """
491*62c56f98SSadaf Ebrahimi        verb = 'save' if self.forward else 'read'
492*62c56f98SSadaf Ebrahimi        tc = test_case.TestCase()
493*62c56f98SSadaf Ebrahimi        tc.set_description(verb + ' ' + key.description)
494*62c56f98SSadaf Ebrahimi        dependencies = psa_information.automatic_dependencies(
495*62c56f98SSadaf Ebrahimi            key.lifetime.string, key.type.string,
496*62c56f98SSadaf Ebrahimi            key.alg.string, key.alg2.string,
497*62c56f98SSadaf Ebrahimi        )
498*62c56f98SSadaf Ebrahimi        dependencies = psa_information.finish_family_dependencies(dependencies, key.bits)
499*62c56f98SSadaf Ebrahimi        dependencies += psa_information.generate_key_dependencies(key.description)
500*62c56f98SSadaf Ebrahimi        dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC')
501*62c56f98SSadaf Ebrahimi        tc.set_dependencies(dependencies)
502*62c56f98SSadaf Ebrahimi        tc.set_function('key_storage_' + verb)
503*62c56f98SSadaf Ebrahimi        if self.forward:
504*62c56f98SSadaf Ebrahimi            extra_arguments = []
505*62c56f98SSadaf Ebrahimi        else:
506*62c56f98SSadaf Ebrahimi            flags = []
507*62c56f98SSadaf Ebrahimi            if self.exercise_key_with_algorithm(key.type, key.bits, key.alg):
508*62c56f98SSadaf Ebrahimi                flags.append('TEST_FLAG_EXERCISE')
509*62c56f98SSadaf Ebrahimi            if 'READ_ONLY' in key.lifetime.string:
510*62c56f98SSadaf Ebrahimi                flags.append('TEST_FLAG_READ_ONLY')
511*62c56f98SSadaf Ebrahimi            extra_arguments = [' | '.join(flags) if flags else '0']
512*62c56f98SSadaf Ebrahimi        tc.set_arguments([key.lifetime.string,
513*62c56f98SSadaf Ebrahimi                          key.type.string, str(key.bits),
514*62c56f98SSadaf Ebrahimi                          key.expected_usage.string,
515*62c56f98SSadaf Ebrahimi                          key.alg.string, key.alg2.string,
516*62c56f98SSadaf Ebrahimi                          '"' + key.material.hex() + '"',
517*62c56f98SSadaf Ebrahimi                          '"' + key.hex() + '"',
518*62c56f98SSadaf Ebrahimi                          *extra_arguments])
519*62c56f98SSadaf Ebrahimi        return tc
520*62c56f98SSadaf Ebrahimi
521*62c56f98SSadaf Ebrahimi    def key_for_lifetime(
522*62c56f98SSadaf Ebrahimi            self,
523*62c56f98SSadaf Ebrahimi            lifetime: str,
524*62c56f98SSadaf Ebrahimi    ) -> StorageTestData:
525*62c56f98SSadaf Ebrahimi        """Construct a test key for the given lifetime."""
526*62c56f98SSadaf Ebrahimi        short = lifetime
527*62c56f98SSadaf Ebrahimi        short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
528*62c56f98SSadaf Ebrahimi                       r'', short)
529*62c56f98SSadaf Ebrahimi        short = crypto_knowledge.short_expression(short)
530*62c56f98SSadaf Ebrahimi        description = 'lifetime: ' + short
531*62c56f98SSadaf Ebrahimi        key = StorageTestData(version=self.version,
532*62c56f98SSadaf Ebrahimi                              id=1, lifetime=lifetime,
533*62c56f98SSadaf Ebrahimi                              type='PSA_KEY_TYPE_RAW_DATA', bits=8,
534*62c56f98SSadaf Ebrahimi                              usage=['PSA_KEY_USAGE_EXPORT'], alg=0, alg2=0,
535*62c56f98SSadaf Ebrahimi                              material=b'L',
536*62c56f98SSadaf Ebrahimi                              description=description)
537*62c56f98SSadaf Ebrahimi        return key
538*62c56f98SSadaf Ebrahimi
539*62c56f98SSadaf Ebrahimi    def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
540*62c56f98SSadaf Ebrahimi        """Generate test keys covering lifetimes."""
541*62c56f98SSadaf Ebrahimi        lifetimes = sorted(self.constructors.lifetimes)
542*62c56f98SSadaf Ebrahimi        expressions = self.constructors.generate_expressions(lifetimes)
543*62c56f98SSadaf Ebrahimi        for lifetime in expressions:
544*62c56f98SSadaf Ebrahimi            # Don't attempt to create or load a volatile key in storage
545*62c56f98SSadaf Ebrahimi            if 'VOLATILE' in lifetime:
546*62c56f98SSadaf Ebrahimi                continue
547*62c56f98SSadaf Ebrahimi            # Don't attempt to create a read-only key in storage,
548*62c56f98SSadaf Ebrahimi            # but do attempt to load one.
549*62c56f98SSadaf Ebrahimi            if 'READ_ONLY' in lifetime and self.forward:
550*62c56f98SSadaf Ebrahimi                continue
551*62c56f98SSadaf Ebrahimi            yield self.key_for_lifetime(lifetime)
552*62c56f98SSadaf Ebrahimi
553*62c56f98SSadaf Ebrahimi    def key_for_usage_flags(
554*62c56f98SSadaf Ebrahimi            self,
555*62c56f98SSadaf Ebrahimi            usage_flags: List[str],
556*62c56f98SSadaf Ebrahimi            short: Optional[str] = None,
557*62c56f98SSadaf Ebrahimi            test_implicit_usage: Optional[bool] = True
558*62c56f98SSadaf Ebrahimi    ) -> StorageTestData:
559*62c56f98SSadaf Ebrahimi        """Construct a test key for the given key usage."""
560*62c56f98SSadaf Ebrahimi        extra_desc = ' without implication' if test_implicit_usage else ''
561*62c56f98SSadaf Ebrahimi        description = 'usage' + extra_desc + ': '
562*62c56f98SSadaf Ebrahimi        key1 = StorageTestData(version=self.version,
563*62c56f98SSadaf Ebrahimi                               id=1, lifetime=0x00000001,
564*62c56f98SSadaf Ebrahimi                               type='PSA_KEY_TYPE_RAW_DATA', bits=8,
565*62c56f98SSadaf Ebrahimi                               expected_usage=usage_flags,
566*62c56f98SSadaf Ebrahimi                               without_implicit_usage=not test_implicit_usage,
567*62c56f98SSadaf Ebrahimi                               usage=usage_flags, alg=0, alg2=0,
568*62c56f98SSadaf Ebrahimi                               material=b'K',
569*62c56f98SSadaf Ebrahimi                               description=description)
570*62c56f98SSadaf Ebrahimi        if short is None:
571*62c56f98SSadaf Ebrahimi            usage_expr = key1.expected_usage.string
572*62c56f98SSadaf Ebrahimi            key1.description += crypto_knowledge.short_expression(usage_expr)
573*62c56f98SSadaf Ebrahimi        else:
574*62c56f98SSadaf Ebrahimi            key1.description += short
575*62c56f98SSadaf Ebrahimi        return key1
576*62c56f98SSadaf Ebrahimi
577*62c56f98SSadaf Ebrahimi    def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
578*62c56f98SSadaf Ebrahimi        """Generate test keys covering usage flags."""
579*62c56f98SSadaf Ebrahimi        known_flags = sorted(self.constructors.key_usage_flags)
580*62c56f98SSadaf Ebrahimi        yield self.key_for_usage_flags(['0'], **kwargs)
581*62c56f98SSadaf Ebrahimi        for usage_flag in known_flags:
582*62c56f98SSadaf Ebrahimi            yield self.key_for_usage_flags([usage_flag], **kwargs)
583*62c56f98SSadaf Ebrahimi        for flag1, flag2 in zip(known_flags,
584*62c56f98SSadaf Ebrahimi                                known_flags[1:] + [known_flags[0]]):
585*62c56f98SSadaf Ebrahimi            yield self.key_for_usage_flags([flag1, flag2], **kwargs)
586*62c56f98SSadaf Ebrahimi
587*62c56f98SSadaf Ebrahimi    def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
588*62c56f98SSadaf Ebrahimi        known_flags = sorted(self.constructors.key_usage_flags)
589*62c56f98SSadaf Ebrahimi        yield self.key_for_usage_flags(known_flags, short='all known')
590*62c56f98SSadaf Ebrahimi
591*62c56f98SSadaf Ebrahimi    def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
592*62c56f98SSadaf Ebrahimi        yield from self.generate_keys_for_usage_flags()
593*62c56f98SSadaf Ebrahimi        yield from self.generate_key_for_all_usage_flags()
594*62c56f98SSadaf Ebrahimi
595*62c56f98SSadaf Ebrahimi    def key_for_type_and_alg(
596*62c56f98SSadaf Ebrahimi            self,
597*62c56f98SSadaf Ebrahimi            kt: crypto_knowledge.KeyType,
598*62c56f98SSadaf Ebrahimi            bits: int,
599*62c56f98SSadaf Ebrahimi            alg: Optional[crypto_knowledge.Algorithm] = None,
600*62c56f98SSadaf Ebrahimi    ) -> StorageTestData:
601*62c56f98SSadaf Ebrahimi        """Construct a test key of the given type.
602*62c56f98SSadaf Ebrahimi
603*62c56f98SSadaf Ebrahimi        If alg is not None, this key allows it.
604*62c56f98SSadaf Ebrahimi        """
605*62c56f98SSadaf Ebrahimi        usage_flags = ['PSA_KEY_USAGE_EXPORT']
606*62c56f98SSadaf Ebrahimi        alg1 = 0 #type: psa_storage.Exprable
607*62c56f98SSadaf Ebrahimi        alg2 = 0
608*62c56f98SSadaf Ebrahimi        if alg is not None:
609*62c56f98SSadaf Ebrahimi            alg1 = alg.expression
610*62c56f98SSadaf Ebrahimi            usage_flags += alg.usage_flags(public=kt.is_public())
611*62c56f98SSadaf Ebrahimi        key_material = kt.key_material(bits)
612*62c56f98SSadaf Ebrahimi        description = 'type: {} {}-bit'.format(kt.short_expression(1), bits)
613*62c56f98SSadaf Ebrahimi        if alg is not None:
614*62c56f98SSadaf Ebrahimi            description += ', ' + alg.short_expression(1)
615*62c56f98SSadaf Ebrahimi        key = StorageTestData(version=self.version,
616*62c56f98SSadaf Ebrahimi                              id=1, lifetime=0x00000001,
617*62c56f98SSadaf Ebrahimi                              type=kt.expression, bits=bits,
618*62c56f98SSadaf Ebrahimi                              usage=usage_flags, alg=alg1, alg2=alg2,
619*62c56f98SSadaf Ebrahimi                              material=key_material,
620*62c56f98SSadaf Ebrahimi                              description=description)
621*62c56f98SSadaf Ebrahimi        return key
622*62c56f98SSadaf Ebrahimi
623*62c56f98SSadaf Ebrahimi    def keys_for_type(
624*62c56f98SSadaf Ebrahimi            self,
625*62c56f98SSadaf Ebrahimi            key_type: str,
626*62c56f98SSadaf Ebrahimi            all_algorithms: List[crypto_knowledge.Algorithm],
627*62c56f98SSadaf Ebrahimi    ) -> Iterator[StorageTestData]:
628*62c56f98SSadaf Ebrahimi        """Generate test keys for the given key type."""
629*62c56f98SSadaf Ebrahimi        kt = crypto_knowledge.KeyType(key_type)
630*62c56f98SSadaf Ebrahimi        for bits in kt.sizes_to_test():
631*62c56f98SSadaf Ebrahimi            # Test a non-exercisable key, as well as exercisable keys for
632*62c56f98SSadaf Ebrahimi            # each compatible algorithm.
633*62c56f98SSadaf Ebrahimi            # To do: test reading a key from storage with an incompatible
634*62c56f98SSadaf Ebrahimi            # or unsupported algorithm.
635*62c56f98SSadaf Ebrahimi            yield self.key_for_type_and_alg(kt, bits)
636*62c56f98SSadaf Ebrahimi            compatible_algorithms = [alg for alg in all_algorithms
637*62c56f98SSadaf Ebrahimi                                     if kt.can_do(alg)]
638*62c56f98SSadaf Ebrahimi            for alg in compatible_algorithms:
639*62c56f98SSadaf Ebrahimi                yield self.key_for_type_and_alg(kt, bits, alg)
640*62c56f98SSadaf Ebrahimi
641*62c56f98SSadaf Ebrahimi    def all_keys_for_types(self) -> Iterator[StorageTestData]:
642*62c56f98SSadaf Ebrahimi        """Generate test keys covering key types and their representations."""
643*62c56f98SSadaf Ebrahimi        key_types = sorted(self.constructors.key_types)
644*62c56f98SSadaf Ebrahimi        all_algorithms = [crypto_knowledge.Algorithm(alg)
645*62c56f98SSadaf Ebrahimi                          for alg in self.constructors.generate_expressions(
646*62c56f98SSadaf Ebrahimi                              sorted(self.constructors.algorithms)
647*62c56f98SSadaf Ebrahimi                          )]
648*62c56f98SSadaf Ebrahimi        for key_type in self.constructors.generate_expressions(key_types):
649*62c56f98SSadaf Ebrahimi            yield from self.keys_for_type(key_type, all_algorithms)
650*62c56f98SSadaf Ebrahimi
651*62c56f98SSadaf Ebrahimi    def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
652*62c56f98SSadaf Ebrahimi        """Generate test keys for the encoding of the specified algorithm."""
653*62c56f98SSadaf Ebrahimi        # These test cases only validate the encoding of algorithms, not
654*62c56f98SSadaf Ebrahimi        # whether the key read from storage is suitable for an operation.
655*62c56f98SSadaf Ebrahimi        # `keys_for_types` generate read tests with an algorithm and a
656*62c56f98SSadaf Ebrahimi        # compatible key.
657*62c56f98SSadaf Ebrahimi        descr = crypto_knowledge.short_expression(alg, 1)
658*62c56f98SSadaf Ebrahimi        usage = ['PSA_KEY_USAGE_EXPORT']
659*62c56f98SSadaf Ebrahimi        key1 = StorageTestData(version=self.version,
660*62c56f98SSadaf Ebrahimi                               id=1, lifetime=0x00000001,
661*62c56f98SSadaf Ebrahimi                               type='PSA_KEY_TYPE_RAW_DATA', bits=8,
662*62c56f98SSadaf Ebrahimi                               usage=usage, alg=alg, alg2=0,
663*62c56f98SSadaf Ebrahimi                               material=b'K',
664*62c56f98SSadaf Ebrahimi                               description='alg: ' + descr)
665*62c56f98SSadaf Ebrahimi        yield key1
666*62c56f98SSadaf Ebrahimi        key2 = StorageTestData(version=self.version,
667*62c56f98SSadaf Ebrahimi                               id=1, lifetime=0x00000001,
668*62c56f98SSadaf Ebrahimi                               type='PSA_KEY_TYPE_RAW_DATA', bits=8,
669*62c56f98SSadaf Ebrahimi                               usage=usage, alg=0, alg2=alg,
670*62c56f98SSadaf Ebrahimi                               material=b'L',
671*62c56f98SSadaf Ebrahimi                               description='alg2: ' + descr)
672*62c56f98SSadaf Ebrahimi        yield key2
673*62c56f98SSadaf Ebrahimi
674*62c56f98SSadaf Ebrahimi    def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
675*62c56f98SSadaf Ebrahimi        """Generate test keys covering algorithm encodings."""
676*62c56f98SSadaf Ebrahimi        algorithms = sorted(self.constructors.algorithms)
677*62c56f98SSadaf Ebrahimi        for alg in self.constructors.generate_expressions(algorithms):
678*62c56f98SSadaf Ebrahimi            yield from self.keys_for_algorithm(alg)
679*62c56f98SSadaf Ebrahimi
680*62c56f98SSadaf Ebrahimi    def generate_all_keys(self) -> Iterator[StorageTestData]:
681*62c56f98SSadaf Ebrahimi        """Generate all keys for the test cases."""
682*62c56f98SSadaf Ebrahimi        yield from self.all_keys_for_lifetimes()
683*62c56f98SSadaf Ebrahimi        yield from self.all_keys_for_usage_flags()
684*62c56f98SSadaf Ebrahimi        yield from self.all_keys_for_types()
685*62c56f98SSadaf Ebrahimi        yield from self.all_keys_for_algorithms()
686*62c56f98SSadaf Ebrahimi
687*62c56f98SSadaf Ebrahimi    def all_test_cases(self) -> Iterator[test_case.TestCase]:
688*62c56f98SSadaf Ebrahimi        """Generate all storage format test cases."""
689*62c56f98SSadaf Ebrahimi        # First build a list of all keys, then construct all the corresponding
690*62c56f98SSadaf Ebrahimi        # test cases. This allows all required information to be obtained in
691*62c56f98SSadaf Ebrahimi        # one go, which is a significant performance gain as the information
692*62c56f98SSadaf Ebrahimi        # includes numerical values obtained by compiling a C program.
693*62c56f98SSadaf Ebrahimi        all_keys = list(self.generate_all_keys())
694*62c56f98SSadaf Ebrahimi        for key in all_keys:
695*62c56f98SSadaf Ebrahimi            if key.location_value() != 0:
696*62c56f98SSadaf Ebrahimi                # Skip keys with a non-default location, because they
697*62c56f98SSadaf Ebrahimi                # require a driver and we currently have no mechanism to
698*62c56f98SSadaf Ebrahimi                # determine whether a driver is available.
699*62c56f98SSadaf Ebrahimi                continue
700*62c56f98SSadaf Ebrahimi            yield self.make_test_case(key)
701*62c56f98SSadaf Ebrahimi
702*62c56f98SSadaf Ebrahimiclass StorageFormatForward(StorageFormat):
703*62c56f98SSadaf Ebrahimi    """Storage format stability test cases for forward compatibility."""
704*62c56f98SSadaf Ebrahimi
705*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information, version: int) -> None:
706*62c56f98SSadaf Ebrahimi        super().__init__(info, version, True)
707*62c56f98SSadaf Ebrahimi
708*62c56f98SSadaf Ebrahimiclass StorageFormatV0(StorageFormat):
709*62c56f98SSadaf Ebrahimi    """Storage format stability test cases for version 0 compatibility."""
710*62c56f98SSadaf Ebrahimi
711*62c56f98SSadaf Ebrahimi    def __init__(self, info: psa_information.Information) -> None:
712*62c56f98SSadaf Ebrahimi        super().__init__(info, 0, False)
713*62c56f98SSadaf Ebrahimi
714*62c56f98SSadaf Ebrahimi    def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
715*62c56f98SSadaf Ebrahimi        """Generate test keys covering usage flags."""
716*62c56f98SSadaf Ebrahimi        yield from super().all_keys_for_usage_flags()
717*62c56f98SSadaf Ebrahimi        yield from self.generate_keys_for_usage_flags(test_implicit_usage=False)
718*62c56f98SSadaf Ebrahimi
719*62c56f98SSadaf Ebrahimi    def keys_for_implicit_usage(
720*62c56f98SSadaf Ebrahimi            self,
721*62c56f98SSadaf Ebrahimi            implyer_usage: str,
722*62c56f98SSadaf Ebrahimi            alg: str,
723*62c56f98SSadaf Ebrahimi            key_type: crypto_knowledge.KeyType
724*62c56f98SSadaf Ebrahimi    ) -> StorageTestData:
725*62c56f98SSadaf Ebrahimi        # pylint: disable=too-many-locals
726*62c56f98SSadaf Ebrahimi        """Generate test keys for the specified implicit usage flag,
727*62c56f98SSadaf Ebrahimi           algorithm and key type combination.
728*62c56f98SSadaf Ebrahimi        """
729*62c56f98SSadaf Ebrahimi        bits = key_type.sizes_to_test()[0]
730*62c56f98SSadaf Ebrahimi        implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
731*62c56f98SSadaf Ebrahimi        usage_flags = ['PSA_KEY_USAGE_EXPORT']
732*62c56f98SSadaf Ebrahimi        material_usage_flags = usage_flags + [implyer_usage]
733*62c56f98SSadaf Ebrahimi        expected_usage_flags = material_usage_flags + [implicit_usage]
734*62c56f98SSadaf Ebrahimi        alg2 = 0
735*62c56f98SSadaf Ebrahimi        key_material = key_type.key_material(bits)
736*62c56f98SSadaf Ebrahimi        usage_expression = crypto_knowledge.short_expression(implyer_usage, 1)
737*62c56f98SSadaf Ebrahimi        alg_expression = crypto_knowledge.short_expression(alg, 1)
738*62c56f98SSadaf Ebrahimi        key_type_expression = key_type.short_expression(1)
739*62c56f98SSadaf Ebrahimi        description = 'implied by {}: {} {} {}-bit'.format(
740*62c56f98SSadaf Ebrahimi            usage_expression, alg_expression, key_type_expression, bits)
741*62c56f98SSadaf Ebrahimi        key = StorageTestData(version=self.version,
742*62c56f98SSadaf Ebrahimi                              id=1, lifetime=0x00000001,
743*62c56f98SSadaf Ebrahimi                              type=key_type.expression, bits=bits,
744*62c56f98SSadaf Ebrahimi                              usage=material_usage_flags,
745*62c56f98SSadaf Ebrahimi                              expected_usage=expected_usage_flags,
746*62c56f98SSadaf Ebrahimi                              without_implicit_usage=True,
747*62c56f98SSadaf Ebrahimi                              alg=alg, alg2=alg2,
748*62c56f98SSadaf Ebrahimi                              material=key_material,
749*62c56f98SSadaf Ebrahimi                              description=description)
750*62c56f98SSadaf Ebrahimi        return key
751*62c56f98SSadaf Ebrahimi
752*62c56f98SSadaf Ebrahimi    def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
753*62c56f98SSadaf Ebrahimi        # pylint: disable=too-many-locals
754*62c56f98SSadaf Ebrahimi        """Match possible key types for sign algorithms."""
755*62c56f98SSadaf Ebrahimi        # To create a valid combination both the algorithms and key types
756*62c56f98SSadaf Ebrahimi        # must be filtered. Pair them with keywords created from its names.
757*62c56f98SSadaf Ebrahimi        incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
758*62c56f98SSadaf Ebrahimi        incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
759*62c56f98SSadaf Ebrahimi        keyword_translation = {
760*62c56f98SSadaf Ebrahimi            'ECDSA': 'ECC',
761*62c56f98SSadaf Ebrahimi            'ED[0-9]*.*' : 'EDWARDS'
762*62c56f98SSadaf Ebrahimi        }
763*62c56f98SSadaf Ebrahimi        exclusive_keywords = {
764*62c56f98SSadaf Ebrahimi            'EDWARDS': 'ECC'
765*62c56f98SSadaf Ebrahimi        }
766*62c56f98SSadaf Ebrahimi        key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
767*62c56f98SSadaf Ebrahimi        algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
768*62c56f98SSadaf Ebrahimi        alg_with_keys = {} #type: Dict[str, List[str]]
769*62c56f98SSadaf Ebrahimi        translation_table = str.maketrans('(', '_', ')')
770*62c56f98SSadaf Ebrahimi        for alg in algorithms:
771*62c56f98SSadaf Ebrahimi            # Generate keywords from the name of the algorithm
772*62c56f98SSadaf Ebrahimi            alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
773*62c56f98SSadaf Ebrahimi            # Translate keywords for better matching with the key types
774*62c56f98SSadaf Ebrahimi            for keyword in alg_keywords.copy():
775*62c56f98SSadaf Ebrahimi                for pattern, replace in keyword_translation.items():
776*62c56f98SSadaf Ebrahimi                    if re.match(pattern, keyword):
777*62c56f98SSadaf Ebrahimi                        alg_keywords.remove(keyword)
778*62c56f98SSadaf Ebrahimi                        alg_keywords.add(replace)
779*62c56f98SSadaf Ebrahimi            # Filter out incompatible algorithms
780*62c56f98SSadaf Ebrahimi            if not alg_keywords.isdisjoint(incompatible_alg_keyword):
781*62c56f98SSadaf Ebrahimi                continue
782*62c56f98SSadaf Ebrahimi
783*62c56f98SSadaf Ebrahimi            for key_type in key_types:
784*62c56f98SSadaf Ebrahimi                # Generate keywords from the of the key type
785*62c56f98SSadaf Ebrahimi                key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
786*62c56f98SSadaf Ebrahimi
787*62c56f98SSadaf Ebrahimi                # Remove ambiguous keywords
788*62c56f98SSadaf Ebrahimi                for keyword1, keyword2 in exclusive_keywords.items():
789*62c56f98SSadaf Ebrahimi                    if keyword1 in key_type_keywords:
790*62c56f98SSadaf Ebrahimi                        key_type_keywords.remove(keyword2)
791*62c56f98SSadaf Ebrahimi
792*62c56f98SSadaf Ebrahimi                if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
793*62c56f98SSadaf Ebrahimi                   not key_type_keywords.isdisjoint(alg_keywords):
794*62c56f98SSadaf Ebrahimi                    if alg in alg_with_keys:
795*62c56f98SSadaf Ebrahimi                        alg_with_keys[alg].append(key_type)
796*62c56f98SSadaf Ebrahimi                    else:
797*62c56f98SSadaf Ebrahimi                        alg_with_keys[alg] = [key_type]
798*62c56f98SSadaf Ebrahimi        return alg_with_keys
799*62c56f98SSadaf Ebrahimi
800*62c56f98SSadaf Ebrahimi    def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
801*62c56f98SSadaf Ebrahimi        """Generate test keys for usage flag extensions."""
802*62c56f98SSadaf Ebrahimi        # Generate a key type and algorithm pair for each extendable usage
803*62c56f98SSadaf Ebrahimi        # flag to generate a valid key for exercising. The key is generated
804*62c56f98SSadaf Ebrahimi        # without usage extension to check the extension compatibility.
805*62c56f98SSadaf Ebrahimi        alg_with_keys = self.gather_key_types_for_sign_alg()
806*62c56f98SSadaf Ebrahimi
807*62c56f98SSadaf Ebrahimi        for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
808*62c56f98SSadaf Ebrahimi            for alg in sorted(alg_with_keys):
809*62c56f98SSadaf Ebrahimi                for key_type in sorted(alg_with_keys[alg]):
810*62c56f98SSadaf Ebrahimi                    # The key types must be filtered to fit the specific usage flag.
811*62c56f98SSadaf Ebrahimi                    kt = crypto_knowledge.KeyType(key_type)
812*62c56f98SSadaf Ebrahimi                    if kt.is_public() and '_SIGN_' in usage:
813*62c56f98SSadaf Ebrahimi                        # Can't sign with a public key
814*62c56f98SSadaf Ebrahimi                        continue
815*62c56f98SSadaf Ebrahimi                    yield self.keys_for_implicit_usage(usage, alg, kt)
816*62c56f98SSadaf Ebrahimi
817*62c56f98SSadaf Ebrahimi    def generate_all_keys(self) -> Iterator[StorageTestData]:
818*62c56f98SSadaf Ebrahimi        yield from super().generate_all_keys()
819*62c56f98SSadaf Ebrahimi        yield from self.all_keys_for_implicit_usage()
820*62c56f98SSadaf Ebrahimi
821*62c56f98SSadaf Ebrahimi
822*62c56f98SSadaf Ebrahimiclass PSATestGenerator(test_data_generation.TestGenerator):
823*62c56f98SSadaf Ebrahimi    """Test generator subclass including PSA targets and info."""
824*62c56f98SSadaf Ebrahimi    # Note that targets whose names contain 'test_format' have their content
825*62c56f98SSadaf Ebrahimi    # validated by `abi_check.py`.
826*62c56f98SSadaf Ebrahimi    targets = {
827*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_generate_key.generated':
828*62c56f98SSadaf Ebrahimi        lambda info: KeyGenerate(info).test_cases_for_key_generation(),
829*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_not_supported.generated':
830*62c56f98SSadaf Ebrahimi        lambda info: KeyTypeNotSupported(info).test_cases_for_not_supported(),
831*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_low_hash.generated':
832*62c56f98SSadaf Ebrahimi        lambda info: crypto_data_tests.HashPSALowLevel(info).all_test_cases(),
833*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_op_fail.generated':
834*62c56f98SSadaf Ebrahimi        lambda info: OpFail(info).all_test_cases(),
835*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_storage_format.current':
836*62c56f98SSadaf Ebrahimi        lambda info: StorageFormatForward(info, 0).all_test_cases(),
837*62c56f98SSadaf Ebrahimi        'test_suite_psa_crypto_storage_format.v0':
838*62c56f98SSadaf Ebrahimi        lambda info: StorageFormatV0(info).all_test_cases(),
839*62c56f98SSadaf Ebrahimi    } #type: Dict[str, Callable[[psa_information.Information], Iterable[test_case.TestCase]]]
840*62c56f98SSadaf Ebrahimi
841*62c56f98SSadaf Ebrahimi    def __init__(self, options):
842*62c56f98SSadaf Ebrahimi        super().__init__(options)
843*62c56f98SSadaf Ebrahimi        self.info = psa_information.Information()
844*62c56f98SSadaf Ebrahimi
845*62c56f98SSadaf Ebrahimi    def generate_target(self, name: str, *target_args) -> None:
846*62c56f98SSadaf Ebrahimi        super().generate_target(name, self.info)
847*62c56f98SSadaf Ebrahimi
848*62c56f98SSadaf Ebrahimi
849*62c56f98SSadaf Ebrahimiif __name__ == '__main__':
850*62c56f98SSadaf Ebrahimi    test_data_generation.main(sys.argv[1:], __doc__, PSATestGenerator)
851