Merge pull request #5635 from gilles-peskine-arm/psa-test-op-fail
PSA: systematically test operation failure
diff --git a/ChangeLog.d/ccm_star_no_tag.txt b/ChangeLog.d/ccm_star_no_tag.txt
new file mode 100644
index 0000000..21e829c
--- /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 dd3554d..f917133 100644
--- a/include/mbedtls/config_psa.h
+++ b/include/mbedtls/config_psa.h
@@ -50,6 +50,12 @@
#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 @@
#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 @@
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 @@
#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 b2c2152..3262e09 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -2333,6 +2333,20 @@
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 @@
/****************************************************************/
#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+static int is_kdf_alg_supported( psa_algorithm_t kdf_alg )
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF)
+ if( PSA_ALG_IS_HKDF( kdf_alg ) )
+ return( 1 );
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
+ if( PSA_ALG_IS_TLS12_PRF( kdf_alg ) )
+ return( 1 );
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if( PSA_ALG_IS_TLS12_PSK_TO_MS( kdf_alg ) )
+ return( 1 );
+#endif
+ 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 );
+}
+
static psa_status_t psa_key_derivation_setup_kdf(
psa_key_derivation_operation_t *operation,
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
-#endif
-#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
- if( PSA_ALG_IS_TLS12_PRF( kdf_alg ) )
- is_kdf_alg_supported = 1;
- else
-#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
-#endif
- is_kdf_alg_supported = 0;
+ if( ! is_kdf_alg_supported( kdf_alg ) )
+ return( PSA_ERROR_NOT_SUPPORTED );
- if( is_kdf_alg_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 @@
{
#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 ae30e5f..fafe68b 100644
--- a/library/psa_crypto_cipher.c
+++ b/library/psa_crypto_cipher.c
@@ -47,39 +47,61 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c38955b..1bd011f 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 @@
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 @@
`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 @@
"""
# 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 c1c9052..6049b74 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -46,6 +46,10 @@
--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 94a834e..0d08f84 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -84,6 +84,10 @@
.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 f42ecd6..1736f24 100755
--- a/tests/scripts/check-generated-files.sh
+++ b/tests/scripts/check-generated-files.sh
@@ -76,7 +76,7 @@
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 @@
# 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 75ef353..ca94d7d 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 @@
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 @@
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 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 @@
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 1a50749..ae24d63 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -2160,7 +2160,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..8b50f10
--- /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 0000000..147c3b7
--- /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