mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2026-03-20 19:21:09 +01:00
Merge pull request #1406 from gilles-peskine-arm/pkcs7-padding-error-timing-leak-3.6
Backport 3.6: Fix timing leak in PSA CBC-PKCS7 decryption
This commit is contained in:
5
ChangeLog.d/pkcs7-padding-error-leak.txt
Normal file
5
ChangeLog.d/pkcs7-padding-error-leak.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Security
|
||||
* Fix a timing side channel in CBC-PKCS7 decryption that could
|
||||
allow an attacker who can submit chosen ciphertexts to recover
|
||||
some plaintexts through a timing-based padding oracle attack.
|
||||
Credits to Beat Heeb from Oberon microsystems AG. CVE-TODO
|
||||
@@ -73,6 +73,8 @@
|
||||
#include "mbedtls/psa_util.h"
|
||||
#include "mbedtls/threading.h"
|
||||
|
||||
#include "constant_time_internal.h"
|
||||
|
||||
#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \
|
||||
defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \
|
||||
defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
|
||||
@@ -4692,13 +4694,27 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation,
|
||||
output_length);
|
||||
|
||||
exit:
|
||||
if (status == PSA_SUCCESS) {
|
||||
status = psa_cipher_abort(operation);
|
||||
} else {
|
||||
*output_length = 0;
|
||||
(void) psa_cipher_abort(operation);
|
||||
/* C99 doesn't allow a declaration to follow a label */;
|
||||
psa_status_t abort_status = psa_cipher_abort(operation);
|
||||
/* Normally abort shouldn't fail unless the operation is in a bad
|
||||
* state, in which case we'd expect finish to fail with the same error.
|
||||
* So it doesn't matter much which call's error code we pick when both
|
||||
* fail. However, in unauthenticated decryption specifically, the
|
||||
* distinction between PSA_SUCCESS and PSA_ERROR_INVALID_PADDING is
|
||||
* security-sensitive (risk of a padding oracle attack), so here we
|
||||
* must not have a code path that depends on the value of status. */
|
||||
if (abort_status != PSA_SUCCESS) {
|
||||
status = abort_status;
|
||||
}
|
||||
|
||||
/* Set *output_length to 0 if status != PSA_SUCCESS, without
|
||||
* leaking the value of status through a timing side channel
|
||||
* (status == PSA_ERROR_INVALID_PADDING is sensitive when doing
|
||||
* unpadded decryption, due to the risk of padding oracle attack). */
|
||||
mbedtls_ct_condition_t success =
|
||||
mbedtls_ct_bool_not(mbedtls_ct_bool(status));
|
||||
*output_length = mbedtls_ct_size_if_else_0(success, *output_length);
|
||||
|
||||
LOCAL_OUTPUT_FREE(output_external, output);
|
||||
|
||||
return status;
|
||||
@@ -4841,13 +4857,17 @@ psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key,
|
||||
|
||||
exit:
|
||||
unlock_status = psa_unregister_read_under_mutex(slot);
|
||||
if (status == PSA_SUCCESS) {
|
||||
if (unlock_status != PSA_SUCCESS) {
|
||||
status = unlock_status;
|
||||
}
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
*output_length = 0;
|
||||
}
|
||||
/* Set *output_length to 0 if status != PSA_SUCCESS, without
|
||||
* leaking the value of status through a timing side channel
|
||||
* (status == PSA_ERROR_INVALID_PADDING is sensitive when doing
|
||||
* unpadded decryption, due to the risk of padding oracle attack). */
|
||||
mbedtls_ct_condition_t success =
|
||||
mbedtls_ct_bool_not(mbedtls_ct_bool(status));
|
||||
*output_length = mbedtls_ct_size_if_else_0(success, *output_length);
|
||||
|
||||
LOCAL_INPUT_FREE(input_external, input);
|
||||
LOCAL_OUTPUT_FREE(output_external, output);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "psa_crypto_cipher.h"
|
||||
#include "psa_crypto_core.h"
|
||||
#include "psa_crypto_random_impl.h"
|
||||
#include "constant_time_internal.h"
|
||||
|
||||
#include "mbedtls/cipher.h"
|
||||
#include "mbedtls/error.h"
|
||||
@@ -551,7 +552,19 @@ psa_status_t mbedtls_psa_cipher_finish(
|
||||
uint8_t *output, size_t output_size, size_t *output_length)
|
||||
{
|
||||
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
|
||||
uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH];
|
||||
size_t invalid_padding = 0;
|
||||
|
||||
/* We will copy output_size bytes from temp_output_buffer to the
|
||||
* output buffer. We can't use *output_length to determine how
|
||||
* much to copy because we must not leak that value through timing
|
||||
* when doing decryption with unpadding. But the underlying function
|
||||
* is not guaranteed to write beyond *output_length. To ensure we don't
|
||||
* leak the former content of the stack to the caller, wipe that
|
||||
* former content. */
|
||||
uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH] = { 0 };
|
||||
if (output_size > sizeof(temp_output_buffer)) {
|
||||
output_size = sizeof(temp_output_buffer);
|
||||
}
|
||||
|
||||
if (operation->ctx.cipher.unprocessed_len != 0) {
|
||||
if (operation->alg == PSA_ALG_ECB_NO_PADDING ||
|
||||
@@ -562,25 +575,34 @@ psa_status_t mbedtls_psa_cipher_finish(
|
||||
}
|
||||
|
||||
status = mbedtls_to_psa_error(
|
||||
mbedtls_cipher_finish(&operation->ctx.cipher,
|
||||
temp_output_buffer,
|
||||
output_length));
|
||||
mbedtls_cipher_finish_padded(&operation->ctx.cipher,
|
||||
temp_output_buffer,
|
||||
output_length,
|
||||
&invalid_padding));
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (*output_length == 0) {
|
||||
if (output_size == 0) {
|
||||
; /* Nothing to copy. Note that output may be NULL in this case. */
|
||||
} else if (output_size >= *output_length) {
|
||||
memcpy(output, temp_output_buffer, *output_length);
|
||||
} else {
|
||||
status = PSA_ERROR_BUFFER_TOO_SMALL;
|
||||
/* Do not use the value of *output_length to determine how much
|
||||
* to copy. When decrypting a padded cipher, the output length is
|
||||
* sensitive, and leaking it could allow a padding oracle attack. */
|
||||
memcpy(output, temp_output_buffer, output_size);
|
||||
}
|
||||
|
||||
status = mbedtls_ct_error_if_else_0(invalid_padding,
|
||||
PSA_ERROR_INVALID_PADDING);
|
||||
mbedtls_ct_condition_t buffer_too_small =
|
||||
mbedtls_ct_uint_lt(output_size, *output_length);
|
||||
status = mbedtls_ct_error_if(buffer_too_small,
|
||||
PSA_ERROR_BUFFER_TOO_SMALL,
|
||||
status);
|
||||
|
||||
exit:
|
||||
mbedtls_platform_zeroize(temp_output_buffer,
|
||||
sizeof(temp_output_buffer));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -701,17 +723,21 @@ psa_status_t mbedtls_psa_cipher_decrypt(
|
||||
&operation,
|
||||
mbedtls_buffer_offset(output, accumulated_length),
|
||||
output_size - accumulated_length, &olength);
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*output_length = accumulated_length + olength;
|
||||
|
||||
exit:
|
||||
if (status == PSA_SUCCESS) {
|
||||
status = mbedtls_psa_cipher_abort(&operation);
|
||||
} else {
|
||||
mbedtls_psa_cipher_abort(&operation);
|
||||
/* C99 doesn't allow a declaration to follow a label */;
|
||||
psa_status_t abort_status = mbedtls_psa_cipher_abort(&operation);
|
||||
/* Normally abort shouldn't fail unless the operation is in a bad
|
||||
* state, in which case we'd expect finish to fail with the same error.
|
||||
* So it doesn't matter much which call's error code we pick when both
|
||||
* fail. However, in unauthenticated decryption specifically, the
|
||||
* distinction between PSA_SUCCESS and PSA_ERROR_INVALID_PADDING is
|
||||
* security-sensitive (risk of a padding oracle attack), so here we
|
||||
* must not have a code path that depends on the value of status. */
|
||||
if (abort_status != PSA_SUCCESS) {
|
||||
status = abort_status;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
39
tests/suites/test_suite_psa_crypto_constant_time.data
Normal file
39
tests/suites/test_suite_psa_crypto_constant_time.data
Normal 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
|
||||
310
tests/suites/test_suite_psa_crypto_constant_time.function
Normal file
310
tests/suites/test_suite_psa_crypto_constant_time.function
Normal file
@@ -0,0 +1,310 @@
|
||||
/* 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
|
||||
|
||||
static int ct_cipher_multipart(psa_cipher_operation_t *operation,
|
||||
const data_t *iv,
|
||||
const data_t *input,
|
||||
size_t output_size,
|
||||
const data_t *expected_output,
|
||||
psa_status_t expected_finish_status)
|
||||
{
|
||||
unsigned char *output = NULL;
|
||||
size_t update_length = SIZE_MAX;
|
||||
size_t finish_length = SIZE_MAX;
|
||||
psa_status_t status;
|
||||
int ok = 0;
|
||||
|
||||
TEST_CALLOC(output, output_size);
|
||||
|
||||
PSA_ASSERT(psa_cipher_set_iv(operation, iv->x, iv->len));
|
||||
status = psa_cipher_update(operation,
|
||||
input->x, input->len,
|
||||
output, output_size, &update_length);
|
||||
if (expected_finish_status == PSA_ERROR_BUFFER_TOO_SMALL &&
|
||||
status == PSA_ERROR_BUFFER_TOO_SMALL) {
|
||||
/* The output buffer is already too small for update. That's ok. */
|
||||
ok = 1;
|
||||
goto exit;
|
||||
} else {
|
||||
PSA_ASSERT(status);
|
||||
}
|
||||
TEST_LE_U(update_length, output_size);
|
||||
TEST_EQUAL(psa_cipher_finish(operation,
|
||||
output + update_length,
|
||||
output_size - update_length,
|
||||
&finish_length),
|
||||
expected_finish_status);
|
||||
|
||||
TEST_CF_PUBLIC(output, output_size);
|
||||
if (expected_finish_status == PSA_SUCCESS) {
|
||||
TEST_MEMORY_COMPARE(expected_output->x, expected_output->len,
|
||||
output, update_length + finish_length);
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
exit:
|
||||
mbedtls_free(output);
|
||||
psa_cipher_abort(operation);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int ct_cipher_decrypt_oneshot(mbedtls_svc_key_id_t key,
|
||||
psa_algorithm_t alg,
|
||||
const data_t *input,
|
||||
size_t output_size,
|
||||
const data_t *expected_output,
|
||||
psa_status_t expected_status)
|
||||
{
|
||||
unsigned char *output = NULL;
|
||||
size_t output_length = SIZE_MAX;
|
||||
int ok = 0;
|
||||
|
||||
TEST_CALLOC(output, output_size);
|
||||
|
||||
TEST_EQUAL(psa_cipher_decrypt(key, alg,
|
||||
input->x, input->len,
|
||||
output, output_size, &output_length),
|
||||
expected_status);
|
||||
|
||||
TEST_CF_PUBLIC(output, output_size);
|
||||
if (expected_status == PSA_SUCCESS) {
|
||||
TEST_MEMORY_COMPARE(expected_output->x, expected_output->len,
|
||||
output, output_length);
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
exit:
|
||||
mbedtls_free(output);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
size_t sufficient_output_size =
|
||||
PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, plaintext->len);
|
||||
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;
|
||||
|
||||
PSA_INIT();
|
||||
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));
|
||||
|
||||
/* Output buffer too small for the actual output */
|
||||
mbedtls_test_set_step(1);
|
||||
PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, plaintext,
|
||||
expected_ciphertext->len - 1,
|
||||
expected_ciphertext,
|
||||
PSA_ERROR_BUFFER_TOO_SMALL)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (expected_ciphertext->len < sufficient_output_size) {
|
||||
/* For a buffer of intermediate size (between the actual output length
|
||||
* and the guaranteed sufficient size), either PSA_SUCCESS or
|
||||
* PSA_ERROR_BUFFER_TOO_SMALL is acceptable. Require what the our
|
||||
* built-in implementation currently does. */
|
||||
psa_status_t intermediate_size_status = PSA_SUCCESS;
|
||||
|
||||
/* Output buffer size just large enough for the actual output
|
||||
* but less than the guaranteed sufficient size */
|
||||
mbedtls_test_set_step(2);
|
||||
PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, plaintext,
|
||||
expected_ciphertext->len,
|
||||
expected_ciphertext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Output buffer size large enough for the actual output
|
||||
* but one less than the guaranteed sufficient size */
|
||||
mbedtls_test_set_step(3);
|
||||
PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, plaintext,
|
||||
sufficient_output_size - 1,
|
||||
expected_ciphertext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Guaranteed sufficient output buffer size */
|
||||
mbedtls_test_set_step(4);
|
||||
PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, plaintext,
|
||||
sufficient_output_size,
|
||||
expected_ciphertext,
|
||||
PSA_SUCCESS)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
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;
|
||||
size_t sufficient_output_size =
|
||||
PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, ciphertext->len);
|
||||
psa_status_t expected_status =
|
||||
expect_invalid_padding ? PSA_ERROR_INVALID_PADDING : PSA_SUCCESS;
|
||||
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;
|
||||
data_t input = { NULL, iv->len + ciphertext->len };
|
||||
|
||||
PSA_INIT();
|
||||
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
|
||||
|
||||
TEST_CALLOC(input.x, input.len);
|
||||
memcpy(input.x, iv->x, iv->len);
|
||||
memcpy(input.x + iv->len, ciphertext->x, ciphertext->len);
|
||||
|
||||
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));
|
||||
|
||||
/* Output buffer too small for the actual output */
|
||||
mbedtls_test_set_step(1);
|
||||
PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, ciphertext,
|
||||
expected_plaintext->len - 1,
|
||||
expected_plaintext,
|
||||
PSA_ERROR_BUFFER_TOO_SMALL)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!ct_cipher_decrypt_oneshot(key, alg, &input,
|
||||
expected_plaintext->len - 1,
|
||||
expected_plaintext,
|
||||
PSA_ERROR_BUFFER_TOO_SMALL)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (expected_plaintext->len < sufficient_output_size) {
|
||||
/* For a buffer of intermediate size (between the actual output length
|
||||
* and the guaranteed sufficient size), either PSA_SUCCESS (or
|
||||
* PSA_ERROR_INVALID_PADDING if the padding is invalid) or
|
||||
* PSA_ERROR_BUFFER_TOO_SMALL is acceptable. Require what the our
|
||||
* built-in implementation currently does. */
|
||||
psa_status_t intermediate_size_status = expected_status;
|
||||
if (alg == PSA_ALG_CBC_PKCS7) {
|
||||
intermediate_size_status = PSA_ERROR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* Output buffer size just large enough for the actual output
|
||||
* but less than the guaranteed sufficient size */
|
||||
mbedtls_test_set_step(2);
|
||||
PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, ciphertext,
|
||||
expected_plaintext->len,
|
||||
expected_plaintext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!ct_cipher_decrypt_oneshot(key, alg, &input,
|
||||
expected_plaintext->len - 1,
|
||||
expected_plaintext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Output buffer size large enough for the actual output
|
||||
* but one less than the guaranteed sufficient size */
|
||||
mbedtls_test_set_step(3);
|
||||
PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, ciphertext,
|
||||
sufficient_output_size - 1,
|
||||
expected_plaintext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!ct_cipher_decrypt_oneshot(key, alg, &input,
|
||||
sufficient_output_size - 1,
|
||||
expected_plaintext,
|
||||
intermediate_size_status)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Guaranteed sufficient output buffer size */
|
||||
mbedtls_test_set_step(4);
|
||||
PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
|
||||
if (!ct_cipher_multipart(&operation, iv, ciphertext,
|
||||
sufficient_output_size,
|
||||
expected_plaintext,
|
||||
expected_status)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!ct_cipher_decrypt_oneshot(key, alg, &input,
|
||||
sufficient_output_size,
|
||||
expected_plaintext,
|
||||
expected_status)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_free(input.x);
|
||||
psa_cipher_abort(&operation);
|
||||
psa_destroy_key(key);
|
||||
PSA_DONE();
|
||||
}
|
||||
/* END_CASE */
|
||||
Reference in New Issue
Block a user