Add constant-time AES-CBC encrypt and decrypt tests through PSA

The main goal is to validate that unpadding is constant-time, including
error reporting.

Use a separate test function, not annotations in the existing function, so
that the functional tests can run on any platform, and we know from test
outcomes where we have run the constant-time tests.

The tests can only be actually constant-time if AES is constant time, since
AES computations are part of what is checked. Thus this requires
hardware-accelerated AES. We can't run our AESNI (or AESCE?) code under
Msan (it doesn't detect when memory is written from assembly code), so these
tests can only be run with Valgrind.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine
2025-07-27 18:10:01 +02:00
parent 9d7d0e63ae
commit bba5d7c439
2 changed files with 200 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
CT encrypt CHACHA20
depends_on:PSA_WANT_ALG_STREAM_CIPHER:PSA_WANT_KEY_TYPE_CHACHA20
ct_cipher_encrypt:PSA_ALG_STREAM_CIPHER:PSA_KEY_TYPE_CHACHA20:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f":"000000000000004a00000000":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"af051e40bba0354981329a806a140eafd258a22a6dcb4bb9f6569cb3efe2deaf837bd87ca20b5ba12081a306af0eb35c41a239d20dfc74c81771560d9c9c1e4b224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167eacd901d7e2bf363"
CT encrypt AES-CTR
depends_on:PSA_WANT_ALG_CTR:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_encrypt:PSA_ALG_CTR:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"dd3b5e5319b7591daab1e1a92687feb2":"396ee84fb75fdbb5c2b13c7fe5a654aa"
CT encrypt AES-CBC-nopad
depends_on:PSA_WANT_ALG_CBC_NO_PADDING:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_encrypt:PSA_ALG_CBC_NO_PADDING:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"49e4e66c89a86b67758df89db9ad6955":"396ee84fb75fdbb5c2b13c7fe5a654aa"
CT encrypt AES-CBC-PKCS7
depends_on:PSA_WANT_ALG_CBC_PKCS7:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_encrypt:PSA_ALG_CBC_PKCS7:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"6bc1bee22e409f96e93d7e117393172a":"a076ec9dfbe47d52afc357336f20743bca7e8a15dc3c776436314293031cd4f3"
CT decrypt CHACHA20
depends_on:PSA_WANT_ALG_STREAM_CIPHER:PSA_WANT_KEY_TYPE_CHACHA20
ct_cipher_decrypt:PSA_ALG_STREAM_CIPHER:PSA_KEY_TYPE_CHACHA20:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f":"000000000000004a00000000":"af051e40bba0354981329a806a140eafd258a22a6dcb4bb9f6569cb3efe2deaf837bd87ca20b5ba12081a306af0eb35c41a239d20dfc74c81771560d9c9c1e4b224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167eacd901d7e2bf363":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":0
CT decrypt AES-CTR
depends_on:PSA_WANT_ALG_CTR:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_decrypt:PSA_ALG_CTR:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"396ee84fb75fdbb5c2b13c7fe5a654aa":"dd3b5e5319b7591daab1e1a92687feb2":0
CT decrypt AES-CBC-nopad
depends_on:PSA_WANT_ALG_CBC_NO_PADDING:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_decrypt:PSA_ALG_CBC_NO_PADDING:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"396ee84fb75fdbb5c2b13c7fe5a654aa":"49e4e66c89a86b67758df89db9ad6955":0
CT decrypt AES-CBC-PKCS7 good
depends_on:PSA_WANT_ALG_CBC_PKCS7:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_decrypt:PSA_ALG_CBC_PKCS7:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"a076ec9dfbe47d52afc357336f20743bca7e8a15dc3c776436314293031cd4f3":"6bc1bee22e409f96e93d7e117393172a":0
CT decrypt AES-CBC-PKCS7 invalid padding @0
depends_on:PSA_WANT_ALG_CBC_PKCS7:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_decrypt:PSA_ALG_CBC_PKCS7:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"a076ec9dfbe47d52afc357336f20743bf42ddf64c420325affb343d5d5f5d5dc":"6bc1bee22e409f96e93d7e117393172a":1
CT decrypt AES-CBC-PKCS7 invalid padding @16
depends_on:PSA_WANT_ALG_CBC_PKCS7:PSA_WANT_KEY_TYPE_AES:HAVE_CONSTANT_TIME_AES
ct_cipher_decrypt:PSA_ALG_CBC_PKCS7:PSA_KEY_TYPE_AES:"2b7e151628aed2a6abf7158809cf4f3c":"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a":"a076ec9dfbe47d52afc357336f20743ba3d6a86d0a9d172eeb1b754512d04416":"6bc1bee22e409f96e93d7e117393172a":1

View File

@@ -0,0 +1,161 @@
/* BEGIN_HEADER */
/* Positive test cases for PSA crypto APIs that assert constant-time
* (more accurately constant-flow) behavior. */
#include <psa/crypto.h>
#include <test/constant_flow.h>
/* Our software AES implementation is not constant-time. For constant-time
* testing involving AES, require a hardware-assisted AES that is
* constant-time.
*
* We assume that if the hardware-assisted version is available in the build,
* it will be available at runtime. The AES tests will fail if run on a
* processor without AESNI/AESCE.
*/
#include "aesce.h"
#include "aesni.h"
#if defined(MBEDTLS_AESCE_HAVE_CODE) || defined(MBEDTLS_AESNI_HAVE_CODE)
#define HAVE_CONSTANT_TIME_AES
#endif
/* END_HEADER */
/* BEGIN_DEPENDENCIES
* depends_on:MBEDTLS_PSA_CRYPTO_C
* END_DEPENDENCIES
*/
/* BEGIN_CASE */
/* Known answer test for cipher multipart encryption.
* There is no known answer test for one-shot encryption because that
* uses a random IV. */
void ct_cipher_encrypt(int alg_arg,
int key_type_arg, const data_t *key_data,
const data_t *iv,
const data_t *plaintext,
const data_t *expected_ciphertext)
{
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 = MBEDTLS_SVC_KEY_ID_INIT;
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
unsigned char *output = NULL;
size_t output_size = PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(plaintext->len);
size_t update_length = SIZE_MAX;
size_t finish_length = SIZE_MAX;
PSA_INIT();
TEST_CALLOC(output, output_size);
TEST_CF_SECRET(key_data->x, key_data->len);
TEST_CF_SECRET(plaintext->x, plaintext->len);
//TEST_ASSERT(key_data->x[0] != 42); // uncomment to trip constant-flow test
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&attributes, alg);
psa_set_key_type(&attributes, key_type);
PSA_ASSERT(psa_import_key(&attributes, key_data->x, key_data->len, &key));
PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
PSA_ASSERT(psa_cipher_set_iv(&operation, iv->x, iv->len));
PSA_ASSERT(psa_cipher_update(&operation,
plaintext->x, plaintext->len,
output, output_size, &update_length));
TEST_LE_U(update_length, output_size);
PSA_ASSERT(psa_cipher_finish(&operation,
output + update_length,
output_size - update_length,
&finish_length));
TEST_CF_PUBLIC(output, output_size);
TEST_MEMORY_COMPARE(expected_ciphertext->x, expected_ciphertext->len,
output, update_length + finish_length);
exit:
mbedtls_free(output);
psa_cipher_abort(&operation);
psa_destroy_key(key);
PSA_DONE();
}
/* END_CASE */
/* BEGIN_CASE */
/* Known answer for cipher decryption (one-shot and multipart).
* Supports good cases and invalid padding cases. */
void ct_cipher_decrypt(int alg_arg,
int key_type_arg, const data_t *key_data,
const data_t *iv,
const data_t *ciphertext,
const data_t *expected_plaintext,
int expect_invalid_padding)
{
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 = MBEDTLS_SVC_KEY_ID_INIT;
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
unsigned char *input = NULL;
unsigned char *output = NULL;
size_t output_size = PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(ciphertext->len);
size_t update_length = SIZE_MAX;
size_t finish_length = SIZE_MAX;
size_t output_length = SIZE_MAX;
psa_status_t status;
PSA_INIT();
TEST_CALLOC(output, output_size);
TEST_CF_SECRET(key_data->x, key_data->len);
TEST_CF_SECRET(ciphertext->x, ciphertext->len);
//TEST_ASSERT(key_data->x[0] != 42); // uncomment to trip constant-flow test
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attributes, alg);
psa_set_key_type(&attributes, key_type);
PSA_ASSERT(psa_import_key(&attributes, key_data->x, key_data->len, &key));
PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
PSA_ASSERT(psa_cipher_set_iv(&operation, iv->x, iv->len));
PSA_ASSERT(psa_cipher_update(&operation,
ciphertext->x, ciphertext->len,
output, output_size, &update_length));
TEST_LE_U(update_length, output_size);
status = psa_cipher_finish(&operation,
output + update_length,
output_size - update_length,
&finish_length);
TEST_CF_PUBLIC(output, output_size);
if (expect_invalid_padding) {
TEST_EQUAL(status, PSA_ERROR_INVALID_PADDING);
} else {
TEST_EQUAL(status, PSA_SUCCESS);
TEST_MEMORY_COMPARE(expected_plaintext->x, expected_plaintext->len,
output, update_length + finish_length);
}
memset(output, 0, output_size);
TEST_CALLOC(input, iv->len + ciphertext->len);
memcpy(input, iv->x, iv->len);
memcpy(input + iv->len, ciphertext->x, ciphertext->len);
status = psa_cipher_decrypt(key, alg,
input, iv->len + ciphertext->len,
output, output_size, &output_length);
TEST_CF_PUBLIC(output, output_size);
if (expect_invalid_padding) {
TEST_EQUAL(status, PSA_ERROR_INVALID_PADDING);
} else {
TEST_EQUAL(status, PSA_SUCCESS);
TEST_MEMORY_COMPARE(expected_plaintext->x, expected_plaintext->len,
output, output_length);
}
exit:
mbedtls_free(input);
mbedtls_free(output);
psa_cipher_abort(&operation);
psa_destroy_key(key);
PSA_DONE();
}
/* END_CASE */