Merge pull request #6802 from gilles-peskine-arm/test_suite_psa_crypto_metadata-20221215
Add metadata tests for CCM* and TLS1.2-ECJPAKE-to-PMS
diff --git a/ChangeLog.d/crypto_config_ccm_star.txt b/ChangeLog.d/crypto_config_ccm_star.txt
new file mode 100644
index 0000000..947014a
--- /dev/null
+++ b/ChangeLog.d/crypto_config_ccm_star.txt
@@ -0,0 +1,3 @@
+Bugfix
+ * List PSA_WANT_ALG_CCM_STAR_NO_TAG in psa/crypto_config.h so that it can
+ be toggled with config.py.
diff --git a/ChangeLog.d/psa_alg_tls12_ecjpake_to_pms-reject_ka.txt b/ChangeLog.d/psa_alg_tls12_ecjpake_to_pms-reject_ka.txt
new file mode 100644
index 0000000..cfea661
--- /dev/null
+++ b/ChangeLog.d/psa_alg_tls12_ecjpake_to_pms-reject_ka.txt
@@ -0,0 +1,4 @@
+Bugfix
+ * The key derivation algorithm PSA_ALG_TLS12_ECJPAKE_TO_PMS cannot be
+ used on a shared secret from a key agreement since its input must be
+ an ECC public key. Reject this properly.
diff --git a/include/mbedtls/config_psa.h b/include/mbedtls/config_psa.h
index 09bc32c..48b2d32 100644
--- a/include/mbedtls/config_psa.h
+++ b/include/mbedtls/config_psa.h
@@ -843,6 +843,8 @@
/* These features are always enabled. */
#define PSA_WANT_KEY_TYPE_DERIVE 1
+#define PSA_WANT_KEY_TYPE_PASSWORD 1
+#define PSA_WANT_KEY_TYPE_PASSWORD_HASH 1
#define PSA_WANT_KEY_TYPE_RAW_DATA 1
#ifdef __cplusplus
diff --git a/include/psa/crypto_config.h b/include/psa/crypto_config.h
index 5ab4fde..e68fac8 100644
--- a/include/psa/crypto_config.h
+++ b/include/psa/crypto_config.h
@@ -57,6 +57,7 @@
#define PSA_WANT_ALG_CBC_NO_PADDING 1
#define PSA_WANT_ALG_CBC_PKCS7 1
#define PSA_WANT_ALG_CCM 1
+#define PSA_WANT_ALG_CCM_STAR_NO_TAG 1
#define PSA_WANT_ALG_CMAC 1
#define PSA_WANT_ALG_CFB 1
#define PSA_WANT_ALG_CHACHA20_POLY1305 1
@@ -115,6 +116,8 @@
#define PSA_WANT_ECC_SECP_R1_521 1
#define PSA_WANT_KEY_TYPE_DERIVE 1
+#define PSA_WANT_KEY_TYPE_PASSWORD 1
+#define PSA_WANT_KEY_TYPE_PASSWORD_HASH 1
#define PSA_WANT_KEY_TYPE_HMAC 1
#define PSA_WANT_KEY_TYPE_AES 1
#define PSA_WANT_KEY_TYPE_ARIA 1
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 0a8949f..a683fdb 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -5168,6 +5168,18 @@
(void) alg;
return PSA_ERROR_NOT_SUPPORTED;
}
+
+static int psa_key_derivation_allows_free_form_secret_input(
+ psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ return 0;
+ }
+#endif
+ (void) kdf_alg;
+ return 1;
+}
#endif /* AT_LEAST_ONE_BUILTIN_KDF */
psa_status_t psa_key_derivation_setup(psa_key_derivation_operation_t *operation,
@@ -5189,6 +5201,9 @@
if (status != PSA_SUCCESS) {
return status;
}
+ if (!psa_key_derivation_allows_free_form_secret_input(kdf_alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
status = psa_key_derivation_setup_kdf(operation, kdf_alg);
#else
return PSA_ERROR_NOT_SUPPORTED;
diff --git a/scripts/mbedtls_dev/crypto_knowledge.py b/scripts/mbedtls_dev/crypto_knowledge.py
index 4dac33f..819d92a 100644
--- a/scripts/mbedtls_dev/crypto_knowledge.py
+++ b/scripts/mbedtls_dev/crypto_knowledge.py
@@ -214,9 +214,7 @@
This function does not currently handle key derivation or PAKE.
"""
#pylint: disable=too-many-branches,too-many-return-statements
- if alg.is_wildcard:
- return False
- if alg.is_invalid_truncation():
+ if not alg.is_valid_for_operation():
return False
if self.head == 'HMAC' and alg.head == 'HMAC':
return True
@@ -248,6 +246,8 @@
# So a public key object with a key agreement algorithm is not
# a valid combination.
return False
+ if alg.is_invalid_key_agreement_with_derivation():
+ return False
if self.head == 'ECC':
assert self.params is not None
eccc = EllipticCurveCategory.from_family(self.params[0])
@@ -414,17 +414,38 @@
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."""
+ def get_key_agreement_derivation(self) -> Optional[str]:
+ """For a combined key agreement and key derivation algorithm, get the derivation part.
+
+ For anything else, return None.
+ """
if self.category != AlgorithmCategory.KEY_AGREEMENT:
- return False
+ return None
m = re.match(r'PSA_ALG_KEY_AGREEMENT\(\w+,\s*(.*)\)\Z', self.expression)
if not m:
- return False
+ return None
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)
+ if re.match(r'(?:0[Xx])?0+\s*\Z', kdf_alg):
+ return None
+ return kdf_alg
+ KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT = frozenset([
+ 'PSA_ALG_TLS12_ECJPAKE_TO_PMS', # secret input in specific format
+ ])
+ def is_valid_key_agreement_with_derivation(self) -> bool:
+ """Whether this is a valid combined key agreement and key derivation algorithm."""
+ kdf_alg = self.get_key_agreement_derivation()
+ if kdf_alg is None:
+ return False
+ return kdf_alg not in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT
+
+ def is_invalid_key_agreement_with_derivation(self) -> bool:
+ """Whether this is an invalid combined key agreement and key derivation algorithm."""
+ kdf_alg = self.get_key_agreement_derivation()
+ if kdf_alg is None:
+ return False
+ return kdf_alg in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT
def short_expression(self, level: int = 0) -> str:
"""Abbreviate the expression, keeping it human-readable.
@@ -498,13 +519,26 @@
return True
return False
+ def is_valid_for_operation(self) -> bool:
+ """Whether this algorithm construction is valid for an operation.
+
+ This function assumes that the algorithm is constructed in a
+ "grammatically" correct way, and only rejects semantically invalid
+ combinations.
+ """
+ if self.is_wildcard:
+ return False
+ if self.is_invalid_truncation():
+ return False
+ return True
+
def can_do(self, category: AlgorithmCategory) -> bool:
"""Whether this algorithm can perform operations in the given category.
"""
if category == self.category:
return True
if category == AlgorithmCategory.KEY_DERIVATION and \
- self.is_key_agreement_with_derivation():
+ self.is_valid_key_agreement_with_derivation():
return True
return False
diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py
index b02ee05..752e7ca 100755
--- a/tests/scripts/generate_psa_tests.py
+++ b/tests/scripts/generate_psa_tests.py
@@ -151,14 +151,16 @@
tc.set_arguments([key_type] + list(args))
return tc
-class NotSupported:
- """Generate test cases for when something is not supported."""
+class KeyTypeNotSupported:
+ """Generate test cases for when a key type is not supported."""
def __init__(self, info: Information) -> None:
self.constructors = info.constructors
ALWAYS_SUPPORTED = frozenset([
'PSA_KEY_TYPE_DERIVE',
+ 'PSA_KEY_TYPE_PASSWORD',
+ 'PSA_KEY_TYPE_PASSWORD_HASH',
'PSA_KEY_TYPE_RAW_DATA',
'PSA_KEY_TYPE_HMAC'
])
@@ -522,7 +524,7 @@
key_type: psa_storage.Expr, bits: int,
alg: psa_storage.Expr
) -> bool:
- """Whether to the given key with the given algorithm.
+ """Whether to exercise the given key with the given algorithm.
Normally only the type and algorithm matter for compatibility, and
this is handled in crypto_knowledge.KeyType.can_do(). This function
@@ -900,7 +902,7 @@
'test_suite_psa_crypto_generate_key.generated':
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(),
+ lambda info: KeyTypeNotSupported(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':
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index 9ced77c..c356142 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -5080,6 +5080,22 @@
depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_ALG_HKDF:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR:MBEDTLS_PK_PARSE_C:PSA_WANT_ECC_SECP_R1_256
derive_input:PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)):PSA_KEY_DERIVATION_INPUT_SALT:PSA_KEY_TYPE_NONE:"":PSA_SUCCESS:PSA_KEY_DERIVATION_INPUT_SECRET:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"c88f01f510d9ac3f70a292daa2316de544e9aab8afe84049c62a9c57862d1433":PSA_SUCCESS:0:UNUSED:"":UNUSED:PSA_KEY_TYPE_NONE:PSA_ERROR_BAD_STATE
+PSA key derivation: TLS12_ECJPAKE_TO_PMS, good input, output too short
+depends_on:PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
+derive_input:PSA_ALG_TLS12_ECJPAKE_TO_PMS:PSA_KEY_DERIVATION_INPUT_SECRET:PSA_KEY_TYPE_NONE:"04aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_SUCCESS:0:UNUSED:"":UNUSED:0:UNUSED:"":UNUSED:PSA_KEY_TYPE_NONE:PSA_ERROR_INVALID_ARGUMENT
+
+PSA key derivation: TLS12_ECJPAKE_TO_PMS, input[0]=0x02
+depends_on:PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
+derive_input:PSA_ALG_TLS12_ECJPAKE_TO_PMS:PSA_KEY_DERIVATION_INPUT_SECRET:PSA_KEY_TYPE_NONE:"02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ERROR_INVALID_ARGUMENT:0:UNUSED:"":UNUSED:0:UNUSED:"":UNUSED:PSA_KEY_TYPE_NONE:PSA_ERROR_BAD_STATE
+
+PSA key derivation: TLS12_ECJPAKE_TO_PMS, input too short
+depends_on:PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
+derive_input:PSA_ALG_TLS12_ECJPAKE_TO_PMS:PSA_KEY_DERIVATION_INPUT_SECRET:PSA_KEY_TYPE_NONE:"04aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ERROR_INVALID_ARGUMENT:0:UNUSED:"":UNUSED:0:UNUSED:"":UNUSED:PSA_KEY_TYPE_NONE:PSA_ERROR_BAD_STATE
+
+PSA key derivation: TLS12_ECJPAKE_TO_PMS, input too long
+depends_on:PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
+derive_input:PSA_ALG_TLS12_ECJPAKE_TO_PMS:PSA_KEY_DERIVATION_INPUT_SECRET:PSA_KEY_TYPE_NONE:"04aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ERROR_INVALID_ARGUMENT:0:UNUSED:"":UNUSED:0:UNUSED:"":UNUSED:PSA_KEY_TYPE_NONE:PSA_ERROR_BAD_STATE
+
PSA key derivation over capacity: HKDF
depends_on:PSA_WANT_ALG_HKDF:PSA_WANT_ALG_SHA_256
derive_over_capacity:PSA_ALG_HKDF(PSA_ALG_SHA_256)
diff --git a/tests/suites/test_suite_psa_crypto_metadata.data b/tests/suites/test_suite_psa_crypto_metadata.data
index bf5f04e..bbd5017 100644
--- a/tests/suites/test_suite_psa_crypto_metadata.data
+++ b/tests/suites/test_suite_psa_crypto_metadata.data
@@ -118,6 +118,10 @@
depends_on:PSA_WANT_ALG_XTS:MBEDTLS_CIPHER_C
cipher_algorithm:PSA_ALG_XTS:0
+Cipher: CCM*
+depends_on:PSA_WANT_ALG_CCM_STAR_NO_TAG
+cipher_algorithm:PSA_ALG_CCM_STAR_NO_TAG:ALG_IS_STREAM_CIPHER
+
AEAD: CCM-AES-128
depends_on:PSA_WANT_KEY_TYPE_AES:PSA_WANT_ALG_CCM
aead_algorithm:PSA_ALG_CCM:ALG_IS_AEAD_ON_BLOCK_CIPHER:16:PSA_KEY_TYPE_AES:128
@@ -286,6 +290,10 @@
depends_on:PSA_WANT_ALG_HKDF_EXPAND:PSA_WANT_ALG_SHA_384
key_derivation_algorithm:PSA_ALG_HKDF_EXPAND( PSA_ALG_SHA_384 ):ALG_IS_HKDF_EXPAND
+Key derivation: TLS1.2 ECJPAKE-to-PMS
+depends_on:PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
+key_derivation_algorithm:PSA_ALG_TLS12_ECJPAKE_TO_PMS:0
+
Key derivation: TLS 1.2 PRF using SHA-256
depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_ALG_TLS12_PRF
key_derivation_algorithm:PSA_ALG_TLS12_PRF( PSA_ALG_SHA_256 ):ALG_IS_TLS12_PRF
@@ -339,6 +347,12 @@
Key type: secret for key derivation
key_type:PSA_KEY_TYPE_DERIVE:KEY_TYPE_IS_UNSTRUCTURED
+Key type: password
+key_type:PSA_KEY_TYPE_PASSWORD:KEY_TYPE_IS_UNSTRUCTURED
+
+Key type: password hash
+key_type:PSA_KEY_TYPE_PASSWORD_HASH:KEY_TYPE_IS_UNSTRUCTURED
+
Block cipher key type: AES
depends_on:PSA_WANT_KEY_TYPE_AES
block_cipher_key_type:PSA_KEY_TYPE_AES:16