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