diff --git a/ChangeLog.d/ccm_star_no_tag.txt b/ChangeLog.d/ccm_star_no_tag.txt new file mode 100644 index 000000000000..21e829c2a943 --- /dev/null +++ b/ChangeLog.d/ccm_star_no_tag.txt @@ -0,0 +1,4 @@ +Bugfix + * Declare or use PSA_WANT_ALG_CCM_STAR_NO_TAG following the general + pattern for PSA_WANT_xxx symbols. Previously you had to specify + PSA_WANT_ALG_CCM for PSA_ALG_CCM_STAR_NO_TAG. diff --git a/include/mbedtls/config_psa.h b/include/mbedtls/config_psa.h index dd3554d33c56..f9171330d7b4 100644 --- a/include/mbedtls/config_psa.h +++ b/include/mbedtls/config_psa.h @@ -50,6 +50,12 @@ extern "C" { #define PSA_WANT_ALG_ECDSA_ANY PSA_WANT_ALG_ECDSA #endif +#if defined(PSA_WANT_ALG_CCM_STAR_NO_TAG) && !defined(PSA_WANT_ALG_CCM) +#define PSA_WANT_ALG_CCM PSA_WANT_ALG_CCM_STAR_NO_TAG +#elif !defined(PSA_WANT_ALG_CCM_STAR_NO_TAG) && defined(PSA_WANT_ALG_CCM) +#define PSA_WANT_ALG_CCM_STAR_NO_TAG PSA_WANT_ALG_CCM +#endif + #if defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) #define PSA_WANT_ALG_RSA_PKCS1V15_SIGN PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW #elif !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) @@ -384,7 +390,8 @@ extern "C" { #endif #endif /* PSA_WANT_ALG_XTS */ -#if defined(PSA_WANT_ALG_ECB_NO_PADDING) +#if defined(PSA_WANT_ALG_ECB_NO_PADDING) && \ + !defined(MBEDTLS_PSA_ACCEL_ALG_ECB_NO_PADDING) #define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1 #endif @@ -411,6 +418,7 @@ extern "C" { defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \ defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) #define MBEDTLS_PSA_BUILTIN_ALG_CCM 1 +#define MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG 1 #define MBEDTLS_CCM_C #endif #endif /* PSA_WANT_ALG_CCM */ @@ -545,7 +553,9 @@ extern "C" { #if defined(MBEDTLS_CCM_C) #define MBEDTLS_PSA_BUILTIN_ALG_CCM 1 +#define MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG 1 #define PSA_WANT_ALG_CCM 1 +#define PSA_WANT_ALG_CCM_STAR_NO_TAG 1 #endif /* MBEDTLS_CCM_C */ #if defined(MBEDTLS_CMAC_C) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index b2c21528c3d9..3262e09e2127 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -2333,6 +2333,20 @@ static psa_status_t psa_mac_finalize_alg_and_key_validation( return( PSA_ERROR_INVALID_ARGUMENT ); } + if( *mac_size > PSA_MAC_MAX_SIZE ) + { + /* PSA_MAC_LENGTH returns the correct length even for a MAC algorithm + * that is disabled in the compile-time configuration. The result can + * therefore be larger than PSA_MAC_MAX_SIZE, which does take the + * configuration into account. In this case, force a return of + * PSA_ERROR_NOT_SUPPORTED here. Otherwise psa_mac_verify(), or + * psa_mac_compute(mac_size=PSA_MAC_MAX_SIZE), would return + * PSA_ERROR_BUFFER_TOO_SMALL for an unsupported algorithm whose MAC size + * is larger than PSA_MAC_MAX_SIZE, which is misleading and which breaks + * systematically generated tests. */ + return( PSA_ERROR_NOT_SUPPORTED ); + } + return( PSA_SUCCESS ); } @@ -5019,50 +5033,75 @@ psa_status_t psa_key_derivation_output_key( const psa_key_attributes_t *attribut /****************************************************************/ #if defined(AT_LEAST_ONE_BUILTIN_KDF) -static psa_status_t psa_key_derivation_setup_kdf( - psa_key_derivation_operation_t *operation, - psa_algorithm_t kdf_alg ) +static int is_kdf_alg_supported( psa_algorithm_t kdf_alg ) { - int is_kdf_alg_supported; - - /* Make sure that operation->ctx is properly zero-initialised. (Macro - * initialisers for this union leave some bytes unspecified.) */ - memset( &operation->ctx, 0, sizeof( operation->ctx ) ); - - /* Make sure that kdf_alg is a supported key derivation algorithm. */ #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) if( PSA_ALG_IS_HKDF( kdf_alg ) ) - is_kdf_alg_supported = 1; - else + return( 1 ); #endif #if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) if( PSA_ALG_IS_TLS12_PRF( kdf_alg ) ) - is_kdf_alg_supported = 1; - else + return( 1 ); #endif #if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) if( PSA_ALG_IS_TLS12_PSK_TO_MS( kdf_alg ) ) - is_kdf_alg_supported = 1; - else + return( 1 ); #endif - is_kdf_alg_supported = 0; + return( 0 ); +} + +static psa_status_t psa_hash_try_support( psa_algorithm_t alg ) +{ + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + psa_status_t status = psa_hash_setup( &operation, alg ); + psa_hash_abort( &operation ); + return( status ); +} - if( is_kdf_alg_supported ) +static psa_status_t psa_key_derivation_setup_kdf( + psa_key_derivation_operation_t *operation, + psa_algorithm_t kdf_alg ) +{ + /* Make sure that operation->ctx is properly zero-initialised. (Macro + * initialisers for this union leave some bytes unspecified.) */ + memset( &operation->ctx, 0, sizeof( operation->ctx ) ); + + /* Make sure that kdf_alg is a supported key derivation algorithm. */ + if( ! is_kdf_alg_supported( kdf_alg ) ) + return( PSA_ERROR_NOT_SUPPORTED ); + + /* All currently supported key derivation algorithms are based on a + * hash algorithm. */ + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH( kdf_alg ); + size_t hash_size = PSA_HASH_LENGTH( hash_alg ); + if( hash_size == 0 ) + return( PSA_ERROR_NOT_SUPPORTED ); + + /* Make sure that hash_alg is a supported hash algorithm. Otherwise + * we might fail later, which is somewhat unfriendly and potentially + * risk-prone. */ + psa_status_t status = psa_hash_try_support( hash_alg ); + if( status != PSA_SUCCESS ) + return( status ); + + if( ( PSA_ALG_IS_TLS12_PRF( kdf_alg ) || + PSA_ALG_IS_TLS12_PSK_TO_MS( kdf_alg ) ) && + ! ( hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384 ) ) { - psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH( kdf_alg ); - size_t hash_size = PSA_HASH_LENGTH( hash_alg ); - if( hash_size == 0 ) - return( PSA_ERROR_NOT_SUPPORTED ); - if( ( PSA_ALG_IS_TLS12_PRF( kdf_alg ) || - PSA_ALG_IS_TLS12_PSK_TO_MS( kdf_alg ) ) && - ! ( hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384 ) ) - { - return( PSA_ERROR_NOT_SUPPORTED ); - } - operation->capacity = 255 * hash_size; - return( PSA_SUCCESS ); + return( PSA_ERROR_NOT_SUPPORTED ); } + operation->capacity = 255 * hash_size; + return( PSA_SUCCESS ); +} + +static psa_status_t psa_key_agreement_try_support( psa_algorithm_t alg ) +{ +#if defined(PSA_WANT_ALG_ECDH) + if( alg == PSA_ALG_ECDH ) + return( PSA_SUCCESS ); +#endif + (void) alg; return( PSA_ERROR_NOT_SUPPORTED ); } #endif /* AT_LEAST_ONE_BUILTIN_KDF */ @@ -5081,6 +5120,10 @@ psa_status_t psa_key_derivation_setup( psa_key_derivation_operation_t *operation { #if defined(AT_LEAST_ONE_BUILTIN_KDF) psa_algorithm_t kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF( alg ); + psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE( alg ); + status = psa_key_agreement_try_support( ka_alg ); + if( status != PSA_SUCCESS ) + return( status ); status = psa_key_derivation_setup_kdf( operation, kdf_alg ); #else return( PSA_ERROR_NOT_SUPPORTED ); diff --git a/library/psa_crypto_cipher.c b/library/psa_crypto_cipher.c index ae30e5fb617d..fafe68b07772 100644 --- a/library/psa_crypto_cipher.c +++ b/library/psa_crypto_cipher.c @@ -47,39 +47,61 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa( { switch( alg ) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER) case PSA_ALG_STREAM_CIPHER: mode = MBEDTLS_MODE_STREAM; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CTR) case PSA_ALG_CTR: mode = MBEDTLS_MODE_CTR; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CFB) case PSA_ALG_CFB: mode = MBEDTLS_MODE_CFB; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_OFB) case PSA_ALG_OFB: mode = MBEDTLS_MODE_OFB; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) case PSA_ALG_ECB_NO_PADDING: mode = MBEDTLS_MODE_ECB; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) case PSA_ALG_CBC_NO_PADDING: mode = MBEDTLS_MODE_CBC; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7) case PSA_ALG_CBC_PKCS7: mode = MBEDTLS_MODE_CBC; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG) case PSA_ALG_CCM_STAR_NO_TAG: mode = MBEDTLS_MODE_CCM_STAR_NO_TAG; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CCM, 0 ): mode = MBEDTLS_MODE_CCM; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_GCM, 0 ): mode = MBEDTLS_MODE_GCM; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CHACHA20_POLY1305, 0 ): mode = MBEDTLS_MODE_CHACHAPOLY; break; +#endif default: return( NULL ); } @@ -91,12 +113,17 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa( switch( key_type ) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES) case PSA_KEY_TYPE_AES: cipher_id_tmp = MBEDTLS_CIPHER_ID_AES; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA) case PSA_KEY_TYPE_ARIA: cipher_id_tmp = MBEDTLS_CIPHER_ID_ARIA; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) case PSA_KEY_TYPE_DES: /* key_bits is 64 for Single-DES, 128 for two-key Triple-DES, * and 192 for three-key Triple-DES. */ @@ -110,12 +137,17 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa( if( key_bits == 128 ) key_bits = 192; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA) case PSA_KEY_TYPE_CAMELLIA: cipher_id_tmp = MBEDTLS_CIPHER_ID_CAMELLIA; break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20) case PSA_KEY_TYPE_CHACHA20: cipher_id_tmp = MBEDTLS_CIPHER_ID_CHACHA20; break; +#endif default: return( NULL ); } @@ -239,6 +271,7 @@ psa_status_t mbedtls_psa_cipher_set_iv( iv, iv_length ) ) ); } +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) /** Process input for which the algorithm is set to ECB mode. * * This requires manual processing, since the PSA API is defined as being @@ -342,6 +375,7 @@ static psa_status_t psa_cipher_update_ecb( exit: return( status ); } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */ psa_status_t mbedtls_psa_cipher_update( mbedtls_psa_cipher_operation_t *operation, @@ -369,6 +403,7 @@ psa_status_t mbedtls_psa_cipher_update( if( output_size < expected_output_size ) return( PSA_ERROR_BUFFER_TOO_SMALL ); +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) if( operation->alg == PSA_ALG_ECB_NO_PADDING ) { /* mbedtls_cipher_update has an API inconsistency: it will only @@ -381,6 +416,7 @@ psa_status_t mbedtls_psa_cipher_update( output_length ); } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */ { status = mbedtls_to_psa_error( mbedtls_cipher_update( &operation->ctx.cipher, input, diff --git a/scripts/mbedtls_dev/crypto_knowledge.py b/scripts/mbedtls_dev/crypto_knowledge.py index c38955b0edfe..1bd011fe2180 100644 --- a/scripts/mbedtls_dev/crypto_knowledge.py +++ b/scripts/mbedtls_dev/crypto_knowledge.py @@ -18,15 +18,45 @@ # See the License for the specific language governing permissions and # limitations under the License. +import enum import re from typing import Dict, Iterable, Optional, Pattern, Tuple from mbedtls_dev.asymmetric_key_data import ASYMMETRIC_KEY_DATA + +BLOCK_CIPHERS = frozenset(['AES', 'ARIA', 'CAMELLIA', 'DES']) +BLOCK_MAC_MODES = frozenset(['CBC_MAC', 'CMAC']) +BLOCK_CIPHER_MODES = frozenset([ + 'CTR', 'CFB', 'OFB', 'XTS', 'CCM_STAR_NO_TAG', + 'ECB_NO_PADDING', 'CBC_NO_PADDING', 'CBC_PKCS7', +]) +BLOCK_AEAD_MODES = frozenset(['CCM', 'GCM']) + +class EllipticCurveCategory(enum.Enum): + """Categorization of elliptic curve families. + + The category of a curve determines what algorithms are defined over it. + """ + + SHORT_WEIERSTRASS = 0 + MONTGOMERY = 1 + TWISTED_EDWARDS = 2 + + @staticmethod + def from_family(family: str) -> 'EllipticCurveCategory': + if family == 'PSA_ECC_FAMILY_MONTGOMERY': + return EllipticCurveCategory.MONTGOMERY + if family == 'PSA_ECC_FAMILY_TWISTED_EDWARDS': + return EllipticCurveCategory.TWISTED_EDWARDS + # Default to SW, which most curves belong to. + return EllipticCurveCategory.SHORT_WEIERSTRASS + + class KeyType: """Knowledge about a PSA key type.""" - def __init__(self, name: str, params: Optional[Iterable[str]] = None): + def __init__(self, name: str, params: Optional[Iterable[str]] = None) -> None: """Analyze a key type. The key type must be specified in PSA syntax. In its simplest form, @@ -62,6 +92,11 @@ def __init__(self, name: str, params: Optional[Iterable[str]] = None): if self.params is not None: self.expression += '(' + ', '.join(self.params) + ')' + m = re.match(r'PSA_KEY_TYPE_(\w+)', self.name) + assert m + self.head = re.sub(r'_(?:PUBLIC_KEY|KEY_PAIR)\Z', r'', m.group(1)) + """The key type macro name, with common prefixes and suffixes stripped.""" + self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name) """The key type macro name for the corresponding key pair type. @@ -69,6 +104,10 @@ def __init__(self, name: str, params: Optional[Iterable[str]] = None): `self.name`. """ + def is_public(self) -> bool: + """Whether the key type is for public keys.""" + return self.name.endswith('_PUBLIC_KEY') + ECC_KEY_SIZES = { 'PSA_ECC_FAMILY_SECP_K1': (192, 224, 256), 'PSA_ECC_FAMILY_SECP_R1': (225, 256, 384, 521), @@ -153,3 +192,210 @@ def is_valid_for_signature(self, usage: str) -> bool: """ # This is just temporaly solution for the implicit usage flags. return re.match(self.KEY_TYPE_FOR_SIGNATURE[usage], self.name) is not None + + def can_do(self, alg: 'Algorithm') -> bool: + """Whether this key type can be used for operations with the given algorithm. + + This function does not currently handle key derivation or PAKE. + """ + #pylint: disable=too-many-return-statements + if alg.is_wildcard: + return False + if self.head == 'HMAC' and alg.head == 'HMAC': + return True + if self.head in BLOCK_CIPHERS and \ + alg.head in frozenset.union(BLOCK_MAC_MODES, + BLOCK_CIPHER_MODES, + BLOCK_AEAD_MODES): + return True + if self.head == 'CHACHA20' and alg.head == 'CHACHA20_POLY1305': + return True + if self.head in {'ARC4', 'CHACHA20'} and \ + alg.head == 'STREAM_CIPHER': + return True + if self.head == 'RSA' and alg.head.startswith('RSA_'): + return True + if self.head == 'ECC': + assert self.params is not None + eccc = EllipticCurveCategory.from_family(self.params[0]) + if alg.head == 'ECDH' and \ + eccc in {EllipticCurveCategory.SHORT_WEIERSTRASS, + EllipticCurveCategory.MONTGOMERY}: + return True + if alg.head == 'ECDSA' and \ + eccc == EllipticCurveCategory.SHORT_WEIERSTRASS: + return True + if alg.head in {'PURE_EDDSA', 'EDDSA_PREHASH'} and \ + eccc == EllipticCurveCategory.TWISTED_EDWARDS: + return True + return False + + +class AlgorithmCategory(enum.Enum): + """PSA algorithm categories.""" + # The numbers are aligned with the category bits in numerical values of + # algorithms. + HASH = 2 + MAC = 3 + CIPHER = 4 + AEAD = 5 + SIGN = 6 + ASYMMETRIC_ENCRYPTION = 7 + KEY_DERIVATION = 8 + KEY_AGREEMENT = 9 + PAKE = 10 + + def requires_key(self) -> bool: + """Whether operations in this category are set up with a key.""" + return self not in {self.HASH, self.KEY_DERIVATION} + + def is_asymmetric(self) -> bool: + """Whether operations in this category involve asymmetric keys.""" + return self in { + self.SIGN, + self.ASYMMETRIC_ENCRYPTION, + self.KEY_AGREEMENT + } + + +class AlgorithmNotRecognized(Exception): + def __init__(self, expr: str) -> None: + super().__init__('Algorithm not recognized: ' + expr) + self.expr = expr + + +class Algorithm: + """Knowledge about a PSA algorithm.""" + + @staticmethod + def determine_base(expr: str) -> str: + """Return an expression for the "base" of the algorithm. + + This strips off variants of algorithms such as MAC truncation. + + This function does not attempt to detect invalid inputs. + """ + m = re.match(r'PSA_ALG_(?:' + r'(?:TRUNCATED|AT_LEAST_THIS_LENGTH)_MAC|' + r'AEAD_WITH_(?:SHORTENED|AT_LEAST_THIS_LENGTH)_TAG' + r')\((.*),[^,]+\)\Z', expr) + if m: + expr = m.group(1) + return expr + + @staticmethod + def determine_head(expr: str) -> str: + """Return the head of an algorithm expression. + + The head is the first (outermost) constructor, without its PSA_ALG_ + prefix, and with some normalization of similar algorithms. + """ + m = re.match(r'PSA_ALG_(?:DETERMINISTIC_)?(\w+)', expr) + if not m: + raise AlgorithmNotRecognized(expr) + head = m.group(1) + if head == 'KEY_AGREEMENT': + m = re.match(r'PSA_ALG_KEY_AGREEMENT\s*\(\s*PSA_ALG_(\w+)', expr) + if not m: + raise AlgorithmNotRecognized(expr) + head = m.group(1) + head = re.sub(r'_ANY\Z', r'', head) + if re.match(r'ED[0-9]+PH\Z', head): + head = 'EDDSA_PREHASH' + return head + + CATEGORY_FROM_HEAD = { + 'SHA': AlgorithmCategory.HASH, + 'SHAKE256_512': AlgorithmCategory.HASH, + 'MD': AlgorithmCategory.HASH, + 'RIPEMD': AlgorithmCategory.HASH, + 'ANY_HASH': AlgorithmCategory.HASH, + 'HMAC': AlgorithmCategory.MAC, + 'STREAM_CIPHER': AlgorithmCategory.CIPHER, + 'CHACHA20_POLY1305': AlgorithmCategory.AEAD, + 'DSA': AlgorithmCategory.SIGN, + 'ECDSA': AlgorithmCategory.SIGN, + 'EDDSA': AlgorithmCategory.SIGN, + 'PURE_EDDSA': AlgorithmCategory.SIGN, + 'RSA_PSS': AlgorithmCategory.SIGN, + 'RSA_PKCS1V15_SIGN': AlgorithmCategory.SIGN, + 'RSA_PKCS1V15_CRYPT': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, + 'RSA_OAEP': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, + 'HKDF': AlgorithmCategory.KEY_DERIVATION, + 'TLS12_PRF': AlgorithmCategory.KEY_DERIVATION, + 'TLS12_PSK_TO_MS': AlgorithmCategory.KEY_DERIVATION, + 'PBKDF': AlgorithmCategory.KEY_DERIVATION, + 'ECDH': AlgorithmCategory.KEY_AGREEMENT, + 'FFDH': AlgorithmCategory.KEY_AGREEMENT, + # KEY_AGREEMENT(...) is a key derivation with a key agreement component + 'KEY_AGREEMENT': AlgorithmCategory.KEY_DERIVATION, + 'JPAKE': AlgorithmCategory.PAKE, + } + for x in BLOCK_MAC_MODES: + CATEGORY_FROM_HEAD[x] = AlgorithmCategory.MAC + for x in BLOCK_CIPHER_MODES: + CATEGORY_FROM_HEAD[x] = AlgorithmCategory.CIPHER + for x in BLOCK_AEAD_MODES: + CATEGORY_FROM_HEAD[x] = AlgorithmCategory.AEAD + + def determine_category(self, expr: str, head: str) -> AlgorithmCategory: + """Return the category of the given algorithm expression. + + This function does not attempt to detect invalid inputs. + """ + prefix = head + while prefix: + if prefix in self.CATEGORY_FROM_HEAD: + return self.CATEGORY_FROM_HEAD[prefix] + if re.match(r'.*[0-9]\Z', prefix): + prefix = re.sub(r'_*[0-9]+\Z', r'', prefix) + else: + prefix = re.sub(r'_*[^_]*\Z', r'', prefix) + raise AlgorithmNotRecognized(expr) + + @staticmethod + def determine_wildcard(expr) -> bool: + """Whether the given algorithm expression is a wildcard. + + This function does not attempt to detect invalid inputs. + """ + if re.search(r'\bPSA_ALG_ANY_HASH\b', expr): + return True + if re.search(r'_AT_LEAST_', expr): + return True + return False + + def __init__(self, expr: str) -> None: + """Analyze an algorithm value. + + The algorithm must be expressed as a C expression containing only + calls to PSA algorithm constructor macros and numeric literals. + + This class is only programmed to handle valid expressions. Invalid + expressions may result in exceptions or in nonsensical results. + """ + self.expression = re.sub(r'\s+', r'', expr) + self.base_expression = self.determine_base(self.expression) + self.head = self.determine_head(self.base_expression) + self.category = self.determine_category(self.base_expression, self.head) + self.is_wildcard = self.determine_wildcard(self.expression) + + def is_key_agreement_with_derivation(self) -> bool: + """Whether this is a combined key agreement and key derivation algorithm.""" + if self.category != AlgorithmCategory.KEY_AGREEMENT: + return False + m = re.match(r'PSA_ALG_KEY_AGREEMENT\(\w+,\s*(.*)\)\Z', self.expression) + if not m: + return False + kdf_alg = m.group(1) + # Assume kdf_alg is either a valid KDF or 0. + return not re.match(r'(?:0[Xx])?0+\s*\Z', kdf_alg) + + def can_do(self, category: AlgorithmCategory) -> bool: + """Whether this algorithm fits the specified operation category.""" + if category == self.category: + return True + if category == AlgorithmCategory.KEY_DERIVATION and \ + self.is_key_agreement_with_derivation(): + return True + return False diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c1c9052e10db..6049b747390c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -46,6 +46,10 @@ if(GEN_FILES) --directory ${CMAKE_CURRENT_BINARY_DIR}/suites DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_psa_tests.py + ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/crypto_knowledge.py + ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/macro_collector.py + ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/psa_storage.py + ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_case.py ${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_config.h ${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_values.h ${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_extra.h diff --git a/tests/Makefile b/tests/Makefile index 94a834ef8565..0d08f845d57d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -84,6 +84,10 @@ generated_files: $(GENERATED_FILES) .SECONDARY: generated_psa_test_data $(GENERATED_DATA_FILES): generated_psa_test_data generated_psa_test_data: scripts/generate_psa_tests.py +generated_psa_test_data: ../scripts/mbedtls_dev/crypto_knowledge.py +generated_psa_test_data: ../scripts/mbedtls_dev/macro_collector.py +generated_psa_test_data: ../scripts/mbedtls_dev/psa_storage.py +generated_psa_test_data: ../scripts/mbedtls_dev/test_case.py ## The generated file only depends on the options that are present in ## crypto_config.h, not on which options are set. To avoid regenerating this ## file all the time when switching between configurations, don't declare diff --git a/tests/scripts/check-generated-files.sh b/tests/scripts/check-generated-files.sh index f42ecd6fb5d1..1736f24d2548 100755 --- a/tests/scripts/check-generated-files.sh +++ b/tests/scripts/check-generated-files.sh @@ -76,7 +76,7 @@ check() for FILE in "$@"; do if [ -e "$FILE" ]; then - cp "$FILE" "$FILE.bak" + cp -p "$FILE" "$FILE.bak" else rm -f "$FILE.bak" fi @@ -86,17 +86,18 @@ check() # Compare the script output to the old files and remove backups for FILE in "$@"; do - if ! diff "$FILE" "$FILE.bak" >/dev/null 2>&1; then + if diff "$FILE" "$FILE.bak" >/dev/null 2>&1; then + # Move the original file back so that $FILE's timestamp doesn't + # change (avoids spurious rebuilds with make). + mv "$FILE.bak" "$FILE" + else echo "'$FILE' was either modified or deleted by '$SCRIPT'" if [ -z "$UPDATE" ]; then exit 1 + else + rm -f "$FILE.bak" fi fi - if [ -z "$UPDATE" ]; then - mv "$FILE.bak" "$FILE" - else - rm -f "$FILE.bak" - fi done if [ -n "$directory" ]; then diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py index 75ef353514a3..ca94d7d3247f 100755 --- a/tests/scripts/generate_psa_tests.py +++ b/tests/scripts/generate_psa_tests.py @@ -21,6 +21,7 @@ # limitations under the License. import argparse +import enum import os import posixpath import re @@ -305,6 +306,145 @@ def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]: kt = crypto_knowledge.KeyType(constr, [curve_family]) yield from self.test_cases_for_key_type_key_generation(kt) +class OpFail: + """Generate test cases for operations that must fail.""" + #pylint: disable=too-few-public-methods + + class Reason(enum.Enum): + NOT_SUPPORTED = 0 + INVALID = 1 + INCOMPATIBLE = 2 + PUBLIC = 3 + + def __init__(self, info: Information) -> None: + self.constructors = info.constructors + key_type_expressions = self.constructors.generate_expressions( + sorted(self.constructors.key_types) + ) + self.key_types = [crypto_knowledge.KeyType(kt_expr) + for kt_expr in key_type_expressions] + + def make_test_case( + self, + alg: crypto_knowledge.Algorithm, + category: crypto_knowledge.AlgorithmCategory, + reason: 'Reason', + kt: Optional[crypto_knowledge.KeyType] = None, + not_deps: FrozenSet[str] = frozenset(), + ) -> test_case.TestCase: + """Construct a failure test case for a one-key or keyless operation.""" + #pylint: disable=too-many-arguments,too-many-locals + tc = test_case.TestCase() + pretty_alg = re.sub(r'PSA_ALG_', r'', alg.expression) + if reason == self.Reason.NOT_SUPPORTED: + short_deps = [re.sub(r'PSA_WANT_ALG_', r'', dep) + for dep in not_deps] + pretty_reason = '!' + '&'.join(sorted(short_deps)) + else: + pretty_reason = reason.name.lower() + if kt: + key_type = kt.expression + pretty_type = re.sub(r'PSA_KEY_TYPE_', r'', key_type) + else: + key_type = '' + pretty_type = '' + tc.set_description('PSA {} {}: {}{}' + .format(category.name.lower(), + pretty_alg, + pretty_reason, + ' with ' + pretty_type if pretty_type else '')) + dependencies = automatic_dependencies(alg.base_expression, key_type) + for i, dep in enumerate(dependencies): + if dep in not_deps: + dependencies[i] = '!' + dep + tc.set_dependencies(dependencies) + tc.set_function(category.name.lower() + '_fail') + arguments = [] + if kt: + key_material = kt.key_material(kt.sizes_to_test()[0]) + arguments += [key_type, test_case.hex_string(key_material)] + arguments.append(alg.expression) + if category.is_asymmetric(): + arguments.append('1' if reason == self.Reason.PUBLIC else '0') + error = ('NOT_SUPPORTED' if reason == self.Reason.NOT_SUPPORTED else + 'INVALID_ARGUMENT') + arguments.append('PSA_ERROR_' + error) + tc.set_arguments(arguments) + return tc + + def no_key_test_cases( + self, + alg: crypto_knowledge.Algorithm, + category: crypto_knowledge.AlgorithmCategory, + ) -> Iterator[test_case.TestCase]: + """Generate failure test cases for keyless operations with the specified algorithm.""" + if alg.can_do(category): + # Compatible operation, unsupported algorithm + for dep in automatic_dependencies(alg.base_expression): + yield self.make_test_case(alg, category, + self.Reason.NOT_SUPPORTED, + not_deps=frozenset([dep])) + else: + # Incompatible operation, supported algorithm + yield self.make_test_case(alg, category, self.Reason.INVALID) + + def one_key_test_cases( + self, + alg: crypto_knowledge.Algorithm, + category: crypto_knowledge.AlgorithmCategory, + ) -> Iterator[test_case.TestCase]: + """Generate failure test cases for one-key operations with the specified algorithm.""" + for kt in self.key_types: + key_is_compatible = kt.can_do(alg) + if key_is_compatible and alg.can_do(category): + # Compatible key and operation, unsupported algorithm + for dep in automatic_dependencies(alg.base_expression): + yield self.make_test_case(alg, category, + self.Reason.NOT_SUPPORTED, + kt=kt, not_deps=frozenset([dep])) + # Public key for a private-key operation + if category.is_asymmetric() and kt.is_public(): + yield self.make_test_case(alg, category, + self.Reason.PUBLIC, + kt=kt) + elif key_is_compatible: + # Compatible key, incompatible operation, supported algorithm + yield self.make_test_case(alg, category, + self.Reason.INVALID, + kt=kt) + elif alg.can_do(category): + # Incompatible key, compatible operation, supported algorithm + yield self.make_test_case(alg, category, + self.Reason.INCOMPATIBLE, + kt=kt) + else: + # Incompatible key and operation. Don't test cases where + # multiple things are wrong, to keep the number of test + # cases reasonable. + pass + + def test_cases_for_algorithm( + self, + alg: crypto_knowledge.Algorithm, + ) -> Iterator[test_case.TestCase]: + """Generate operation failure test cases for the specified algorithm.""" + for category in crypto_knowledge.AlgorithmCategory: + if category == crypto_knowledge.AlgorithmCategory.PAKE: + # PAKE operations are not implemented yet + pass + elif category.requires_key(): + yield from self.one_key_test_cases(alg, category) + else: + yield from self.no_key_test_cases(alg, category) + + def all_test_cases(self) -> Iterator[test_case.TestCase]: + """Generate all test cases for operations that must fail.""" + algorithms = sorted(self.constructors.algorithms) + for expr in self.constructors.generate_expressions(algorithms): + alg = crypto_knowledge.Algorithm(expr) + yield from self.test_cases_for_algorithm(alg) + + class StorageKey(psa_storage.Key): """Representation of a key for storage format testing.""" @@ -443,51 +583,41 @@ def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]: continue yield self.key_for_lifetime(lifetime) - def keys_for_usage_flags( + def key_for_usage_flags( self, usage_flags: List[str], short: Optional[str] = None, - test_implicit_usage: Optional[bool] = False - ) -> Iterator[StorageTestData]: + test_implicit_usage: Optional[bool] = True + ) -> StorageTestData: """Construct a test key for the given key usage.""" usage = ' | '.join(usage_flags) if usage_flags else '0' if short is None: short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage) - extra_desc = ' with implication' if test_implicit_usage else '' + extra_desc = ' without implication' if test_implicit_usage else '' description = 'usage' + extra_desc + ': ' + short key1 = StorageTestData(version=self.version, id=1, lifetime=0x00000001, type='PSA_KEY_TYPE_RAW_DATA', bits=8, expected_usage=usage, + without_implicit_usage=not test_implicit_usage, usage=usage, alg=0, alg2=0, material=b'K', description=description) - yield key1 - - if test_implicit_usage: - description = 'usage without implication' + ': ' + short - key2 = StorageTestData(version=self.version, - id=1, lifetime=0x00000001, - type='PSA_KEY_TYPE_RAW_DATA', bits=8, - without_implicit_usage=True, - usage=usage, alg=0, alg2=0, - material=b'K', - description=description) - yield key2 + return key1 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]: """Generate test keys covering usage flags.""" known_flags = sorted(self.constructors.key_usage_flags) - yield from self.keys_for_usage_flags(['0'], **kwargs) + yield self.key_for_usage_flags(['0'], **kwargs) for usage_flag in known_flags: - yield from self.keys_for_usage_flags([usage_flag], **kwargs) + yield self.key_for_usage_flags([usage_flag], **kwargs) for flag1, flag2 in zip(known_flags, known_flags[1:] + [known_flags[0]]): - yield from self.keys_for_usage_flags([flag1, flag2], **kwargs) + yield self.key_for_usage_flags([flag1, flag2], **kwargs) def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]: known_flags = sorted(self.constructors.key_usage_flags) - yield from self.keys_for_usage_flags(known_flags, short='all known') + yield self.key_for_usage_flags(known_flags, short='all known') def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]: yield from self.generate_keys_for_usage_flags() @@ -593,8 +723,8 @@ def __init__(self, info: Information) -> None: def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]: """Generate test keys covering usage flags.""" - yield from self.generate_keys_for_usage_flags(test_implicit_usage=True) - yield from self.generate_key_for_all_usage_flags() + yield from super().all_keys_for_usage_flags() + yield from self.generate_keys_for_usage_flags(test_implicit_usage=False) def keys_for_implicit_usage( self, @@ -732,6 +862,8 @@ def write_test_data_file(self, basename: str, lambda info: KeyGenerate(info).test_cases_for_key_generation(), 'test_suite_psa_crypto_not_supported.generated': lambda info: NotSupported(info).test_cases_for_not_supported(), + 'test_suite_psa_crypto_op_fail.generated': + lambda info: OpFail(info).all_test_cases(), 'test_suite_psa_crypto_storage_format.current': lambda info: StorageFormatForward(info, 0).all_test_cases(), 'test_suite_psa_crypto_storage_format.v0': diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 1a50749c3827..ae24d63eb200 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -2160,7 +2160,7 @@ depends_on:PSA_WANT_ALG_CBC_NO_PADDING:PSA_WANT_KEY_TYPE_DES cipher_encrypt_validation:PSA_ALG_CBC_NO_PADDING:PSA_KEY_TYPE_DES:"01020407080b0d0ec1c2c4c7c8cbcdce31323437383b3d3e":"eda4011239bc3ac9" PSA symmetric encrypt validation: CCM*-no-tag, 15 bytes, good -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_encrypt_validation:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"d24a3d3dde8c84830280cb87abad0bb3":"6bc1bee22e409f96e93d7e11739317" PSA symmetric encrypt multipart: AES-ECB, 0 bytes, good @@ -2224,7 +2224,7 @@ depends_on:PSA_WANT_ALG_ECB_NO_PADDING:PSA_WANT_KEY_TYPE_DES cipher_encrypt_multipart:PSA_ALG_ECB_NO_PADDING:PSA_KEY_TYPE_DES:"01020407080b0d0ec1c2c4c7c8cbcdce31323437383b3d3e":"":"c78e2b38139610e3":8:8:0:"817ca7d69b80d86a":PSA_SUCCESS PSA symmetric encrypt multipart: CCM*-no-tag, AES, 24 bytes, good -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_encrypt_multipart:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"d24a3d3dde8c84830280cb87abad0bb3":"f1100035bb24a8d26004e0e24b":"7c86135ed9c2a515aaae0e9a208133897269220f30870006":10:10:14:"1faeb0ee2ca2cd52f0aa3966578344f24e69b742c4ab37ab":PSA_SUCCESS PSA cipher decrypt: without initialization @@ -2260,7 +2260,7 @@ depends_on:PSA_WANT_ALG_CBC_NO_PADDING:PSA_WANT_KEY_TYPE_AES cipher_decrypt_fail:PSA_ALG_CBC_NO_PADDING:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"6bc1bee223":PSA_ERROR_INVALID_ARGUMENT PSA symetric decrypt: CCM*-no-tag, input too short (15 bytes) -depends_on:MBEDTLS_AES_C:MBEDTLS_CCM_C +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:MBEDTLS_AES_C cipher_decrypt_fail:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"19ebfde2d5468ba0a3031bde629b11fd":"5a8aa485c316e9":"2a2a2a2a2a2a2a2a":PSA_ERROR_INVALID_ARGUMENT PSA symmetric decrypt: AES-ECB, 0 bytes, good @@ -2312,7 +2312,7 @@ depends_on:PSA_WANT_ALG_ECB_NO_PADDING:PSA_WANT_KEY_TYPE_DES cipher_decrypt:PSA_ALG_ECB_NO_PADDING:PSA_KEY_TYPE_DES:"01020407080b0d0ec1c2c4c7c8cbcdce31323437383b3d3e":"":"817ca7d69b80d86a":"c78e2b38139610e3" PSA symmetric decrypt: CCM*-no-tag, NIST DVPT AES-128 #15 -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_decrypt:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"90929a4b0ac65b350ad1591611fe4829":"5a8aa485c316e9403aff859fbb":"4bfe4e35784f0a65b545477e5e2f4bae0e1e6fa717eaf2cb":"a16a2e741f1cd9717285b6d882c1fc53655e9773761ad697" PSA symmetric decrypt multipart: AES-ECB, 0 bytes, good @@ -2376,7 +2376,7 @@ depends_on:PSA_WANT_ALG_ECB_NO_PADDING:PSA_WANT_KEY_TYPE_DES cipher_decrypt_multipart:PSA_ALG_ECB_NO_PADDING:PSA_KEY_TYPE_DES:"01020407080b0d0ec1c2c4c7c8cbcdce31323437383b3d3e":"":"817ca7d69b80d86a":8:8:0:"c78e2b38139610e3":PSA_SUCCESS PSA symmetric decrypt multipart: CCM*-no-tag, 24 bytes, good -depends_on:PSA_WANT_ALG_ECB_NO_PADDING:PSA_WANT_KEY_TYPE_DES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_decrypt_multipart:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"197afb02ffbd8f699dacae87094d5243":"5a8aa485c316e9403aff859fbb":"4a550134f94455979ec4bf89ad2bd80d25a77ae94e456134":10:10:14:"a16a2e741f1cd9717285b6d882c1fc53655e9773761ad697":PSA_SUCCESS PSA symmetric encrypt/decrypt: AES-ECB, 16 bytes, good @@ -2400,19 +2400,19 @@ depends_on:PSA_WANT_ALG_CTR:PSA_WANT_KEY_TYPE_AES cipher_verify_output:PSA_ALG_CTR:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"6bc1bee22e409f96e93d7e117393172a" PSA symmetric encrypt/decrypt: CCM*-no-tag, AES -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_verify_output:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"6bc1bee22e409f96e93d7e117393172a" CCM*-no-tag encrypt, iv_length = 14, bad -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_encrypt_validate_iv_length:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"90929a4b0ac65b350ad1591611fe4829":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":14:PSA_ERROR_INVALID_ARGUMENT CCM*-no-tag encrypt, iv_length = 13, good -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_encrypt_validate_iv_length:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"90929a4b0ac65b350ad1591611fe4829":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":13:PSA_SUCCESS CCM*-no-tag encrypt, iv_length = 12, bad -depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES +depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG:PSA_WANT_KEY_TYPE_AES cipher_encrypt_validate_iv_length:PSA_ALG_CCM_STAR_NO_TAG:PSA_KEY_TYPE_AES:"90929a4b0ac65b350ad1591611fe4829":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":12:PSA_ERROR_INVALID_ARGUMENT PSA symmetric encryption multipart: AES-ECB, 16+16 bytes diff --git a/tests/suites/test_suite_psa_crypto_op_fail.function b/tests/suites/test_suite_psa_crypto_op_fail.function new file mode 100644 index 000000000000..8b50f102918d --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_op_fail.function @@ -0,0 +1,390 @@ +/* BEGIN_HEADER */ + +#include "psa/crypto.h" +#include "test/psa_crypto_helpers.h" + +static int test_equal_status( const char *test, + int line_no, const char* filename, + psa_status_t value1, + psa_status_t value2 ) +{ + if( ( value1 == PSA_ERROR_INVALID_ARGUMENT && + value2 == PSA_ERROR_NOT_SUPPORTED ) || + ( value1 == PSA_ERROR_NOT_SUPPORTED && + value2 == PSA_ERROR_INVALID_ARGUMENT ) ) + { + return( 1 ); + } + return( mbedtls_test_equal( test, line_no, filename, value1, value2 ) ); +} + +/** Like #TEST_EQUAL, but expects #psa_status_t values and treats + * #PSA_ERROR_INVALID_ARGUMENT and #PSA_ERROR_NOT_SUPPORTED as + * interchangeable. + * + * This test suite currently allows NOT_SUPPORTED and INVALID_ARGUMENT + * to be interchangeable in places where the library's behavior does not + * match the strict expectations of the test case generator. In the long + * run, it would be better to clarify the expectations and reconcile the + * library and the test case generator. + */ +#define TEST_STATUS( expr1, expr2 ) \ + do { \ + if( ! test_equal_status( #expr1 " == " #expr2, __LINE__, __FILE__, \ + expr1, expr2 ) ) \ + goto exit; \ + } while( 0 ) + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PSA_CRYPTO_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void hash_fail( int alg_arg, int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_algorithm_t alg = alg_arg; + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + uint8_t input[1] = {'A'}; + uint8_t output[PSA_HASH_MAX_SIZE] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + TEST_EQUAL( expected_status, + psa_hash_setup( &operation, alg ) ); + TEST_EQUAL( expected_status, + psa_hash_compute( alg, input, sizeof( input ), + output, sizeof( output ), &length ) ); + TEST_EQUAL( expected_status, + psa_hash_compare( alg, input, sizeof( input ), + output, sizeof( output ) ) ); + +exit: + psa_hash_abort( &operation ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mac_fail( int key_type_arg, data_t *key_data, + int alg_arg, int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t input[1] = {'A'}; + uint8_t output[PSA_MAC_MAX_SIZE] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_VERIFY_HASH ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + + TEST_STATUS( expected_status, + psa_mac_sign_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_mac_verify_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_mac_compute( key_id, alg, + input, sizeof( input ), + output, sizeof( output ), &length ) ); + TEST_STATUS( expected_status, + psa_mac_verify( key_id, alg, + input, sizeof( input ), + output, sizeof( output ) ) ); + +exit: + psa_mac_abort( &operation ); + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void cipher_fail( int key_type_arg, data_t *key_data, + int alg_arg, int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t input[1] = {'A'}; + uint8_t output[64] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_ENCRYPT | + PSA_KEY_USAGE_DECRYPT ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + + TEST_STATUS( expected_status, + psa_cipher_encrypt_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_cipher_decrypt_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_cipher_encrypt( key_id, alg, + input, sizeof( input ), + output, sizeof( output ), &length ) ); + TEST_STATUS( expected_status, + psa_cipher_decrypt( key_id, alg, + input, sizeof( input ), + output, sizeof( output ), &length ) ); + +exit: + psa_cipher_abort( &operation ); + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void aead_fail( int key_type_arg, data_t *key_data, + int alg_arg, int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t input[16] = "ABCDEFGHIJKLMNO"; + uint8_t output[64] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_ENCRYPT | + PSA_KEY_USAGE_DECRYPT ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + + TEST_STATUS( expected_status, + psa_aead_encrypt_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_aead_decrypt_setup( &operation, key_id, alg ) ); + TEST_STATUS( expected_status, + psa_aead_encrypt( key_id, alg, + input, sizeof( input ), + NULL, 0, input, sizeof( input ), + output, sizeof( output ), &length ) ); + TEST_STATUS( expected_status, + psa_aead_decrypt( key_id, alg, + input, sizeof( input ), + NULL, 0, input, sizeof( input ), + output, sizeof( output ), &length ) ); + +exit: + psa_aead_abort( &operation ); + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void sign_fail( int key_type_arg, data_t *key_data, + int alg_arg, int private_only, + int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t input[1] = {'A'}; + uint8_t output[PSA_SIGNATURE_MAX_SIZE] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_VERIFY_HASH ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + + TEST_STATUS( expected_status, + psa_sign_hash( key_id, alg, + input, sizeof( input ), + output, sizeof( output ), &length ) ); + if( ! private_only ) + { + /* Determine a plausible signature size to avoid an INVALID_SIGNATURE + * error based on this. */ + PSA_ASSERT( psa_get_key_attributes( key_id, &attributes ) ); + size_t key_bits = psa_get_key_bits( &attributes ); + size_t output_length = sizeof( output ); + if( PSA_KEY_TYPE_IS_RSA( key_type ) ) + output_length = PSA_BITS_TO_BYTES( key_bits ); + else if( PSA_KEY_TYPE_IS_ECC( key_type ) ) + output_length = 2 * PSA_BITS_TO_BYTES( key_bits ); + TEST_ASSERT( output_length <= sizeof( output ) ); + TEST_STATUS( expected_status, + psa_verify_hash( key_id, alg, + input, sizeof( input ), + output, output_length ) ); + } + +exit: + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void asymmetric_encryption_fail( int key_type_arg, data_t *key_data, + int alg_arg, int private_only, + int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t plaintext[PSA_ASYMMETRIC_DECRYPT_OUTPUT_MAX_SIZE] = {0}; + uint8_t ciphertext[PSA_ASYMMETRIC_ENCRYPT_OUTPUT_MAX_SIZE] = {0}; + size_t length = SIZE_MAX; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_ENCRYPT | + PSA_KEY_USAGE_DECRYPT ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + + if( ! private_only ) + { + TEST_STATUS( expected_status, + psa_asymmetric_encrypt( key_id, alg, + plaintext, 1, + NULL, 0, + ciphertext, sizeof( ciphertext ), + &length ) ); + } + TEST_STATUS( expected_status, + psa_asymmetric_decrypt( key_id, alg, + ciphertext, sizeof( ciphertext ), + NULL, 0, + plaintext, sizeof( plaintext ), + &length ) ); + +exit: + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void key_derivation_fail( int alg_arg, int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_algorithm_t alg = alg_arg; + psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + + PSA_INIT( ); + + TEST_EQUAL( expected_status, + psa_key_derivation_setup( &operation, alg ) ); + +exit: + psa_key_derivation_abort( &operation ); + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void key_agreement_fail( int key_type_arg, data_t *key_data, + int alg_arg, int private_only, + int expected_status_arg ) +{ + psa_status_t expected_status = expected_status_arg; + psa_key_type_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + uint8_t public_key[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE] = {0}; + size_t public_key_length = SIZE_MAX; + uint8_t output[PSA_SIGNATURE_MAX_SIZE] = {0}; + size_t length = SIZE_MAX; + psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + + PSA_INIT( ); + + psa_set_key_type( &attributes, key_type ); + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_DERIVE ); + psa_set_key_algorithm( &attributes, alg ); + PSA_ASSERT( psa_import_key( &attributes, + key_data->x, key_data->len, + &key_id ) ); + if( PSA_KEY_TYPE_IS_KEY_PAIR( key_type ) || + PSA_KEY_TYPE_IS_PUBLIC_KEY( key_type ) ) + { + PSA_ASSERT( psa_export_public_key( key_id, + public_key, sizeof( public_key ), + &public_key_length ) ); + } + + TEST_STATUS( expected_status, + psa_raw_key_agreement( alg, key_id, + public_key, public_key_length, + output, sizeof( output ), &length ) ); + +#if defined(PSA_WANT_ALG_HKDF) && defined(PSA_WANT_ALG_SHA_256) + PSA_ASSERT( psa_key_derivation_setup( &operation, + PSA_ALG_HKDF( PSA_ALG_SHA_256 ) ) ); + TEST_STATUS( expected_status, + psa_key_derivation_key_agreement( + &operation, + PSA_KEY_DERIVATION_INPUT_SECRET, + key_id, + public_key, public_key_length ) ); +#endif + + /* There are no public-key operations. */ + (void) private_only; + +exit: + psa_key_derivation_abort( &operation ); + psa_destroy_key( key_id ); + psa_reset_key_attributes( &attributes ); + PSA_DONE( ); +} +/* END_CASE */ diff --git a/tests/suites/test_suite_psa_crypto_op_fail.misc.data b/tests/suites/test_suite_psa_crypto_op_fail.misc.data new file mode 100644 index 000000000000..147c3b76fd82 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_op_fail.misc.data @@ -0,0 +1,15 @@ +# Most operation failure test cases are automatically generated in +# test_suite_psa_crypto_op_fail.generated.data. The manually written +# test cases in this file are only intended to help with debugging the +# test code. + +PSA hash: invalid algorithm +hash_fail:PSA_ALG_ECDSA_ANY:PSA_ERROR_INVALID_ARGUMENT + +PSA sign RSA_PSS(SHA_256): incompatible key type +depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_AES +sign_fail:PSA_KEY_TYPE_AES:"48657265006973206b6579a064617461":PSA_ALG_RSA_PSS(PSA_ALG_SHA_256):0:PSA_ERROR_INVALID_ARGUMENT + +PSA sign RSA_PSS(SHA_256): RSA_PSS not enabled, key pair +depends_on:!PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR +sign_fail:PSA_KEY_TYPE_RSA_KEY_PAIR:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_ALG_RSA_PSS(PSA_ALG_SHA_256):0:PSA_ERROR_NOT_SUPPORTED