/* BEGIN_HEADER */
/*
 * Test suite for the PSA hash built-in driver
 *
 * This test suite exercises some aspects of the built-in PSA driver for
 * hash algorithms (psa_crypto_hash.c). This code is mostly tested via
 * the application interface (above the PSA API layer) and via tests of
 * individual hash modules. The goal of this test suite is to ensure that
 * the driver dispatch layer behaves correctly even when not invoked via
 * the API layer, but directly from another driver.
 *
 * This test suite is currently incomplete. It focuses on non-regression
 * tests for past bugs or near misses.
 */

#include <psa_crypto_hash.h>

/* END_HEADER */

/* BEGIN_DEPENDENCIES
 * depends_on:MBEDTLS_PSA_BUILTIN_HASH
 * END_DEPENDENCIES
 */

/* BEGIN_CASE */
void hash_valid_one_shot(int alg_arg, data_t *input,
                         data_t *expected)
{
    psa_algorithm_t alg = alg_arg;
    uint8_t *output = NULL;
    size_t output_size = expected->len;
    size_t length = SIZE_MAX;

    /* Nominal case */
    ASSERT_ALLOC(output, output_size);
    TEST_EQUAL(mbedtls_psa_hash_compute(alg, input->x, input->len,
                                        output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected->x, expected->len, output, length);
    mbedtls_free(output);
    output = NULL;

    /* Larger output buffer */
    output_size = expected->len + 1;
    ASSERT_ALLOC(output, output_size);
    TEST_EQUAL(mbedtls_psa_hash_compute(alg, input->x, input->len,
                                        output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected->x, expected->len, output, length);
    mbedtls_free(output);
    output = NULL;

    /* We don't test with a smaller output buffer because this isn't
     * guaranteed to work: the core must pass a sufficiently large
     * output buffer to the driver. */

exit:
    mbedtls_free(output);
}
/* END_CASE */

/* BEGIN_CASE */
void hash_valid_multipart(int alg_arg,
                          data_t *input1, data_t *expected1,
                          data_t *input2, data_t *expected2)
{
    psa_algorithm_t alg = alg_arg;
    uint8_t *output = NULL;
    size_t output_size = expected1->len;
    size_t length = SIZE_MAX;
    mbedtls_psa_hash_operation_t operation0; // original
    memset(&operation0, 0, sizeof(operation0));
    mbedtls_psa_hash_operation_t clone_start; // cloned after setup
    memset(&clone_start, 0, sizeof(clone_start));
    mbedtls_psa_hash_operation_t clone_middle; // cloned between updates
    memset(&clone_middle, 0, sizeof(clone_middle));
    mbedtls_psa_hash_operation_t clone_end; // cloned before finish
    memset(&clone_end, 0, sizeof(clone_end));
    mbedtls_psa_hash_operation_t clone_more; // cloned before finish
    memset(&clone_more, 0, sizeof(clone_more));

    /* Nominal case with two update calls */
    ASSERT_ALLOC(output, output_size);
    TEST_EQUAL(mbedtls_psa_hash_setup(&operation0, alg),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_clone(&operation0, &clone_start),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_update(&operation0, input1->x, input1->len),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_clone(&operation0, &clone_middle),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_update(&operation0, input2->x, input2->len),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_clone(&operation0, &clone_end),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_finish(&operation0,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected2->x, expected2->len, output, length);

    /* Nominal case with an operation cloned after setup */
    memset(output, 0, output_size);
    TEST_EQUAL(mbedtls_psa_hash_update(&clone_start, input1->x, input1->len),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_finish(&clone_start,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected1->x, expected1->len, output, length);

    /* Nominal case with an operation cloned between updates */
    memset(output, 0, output_size);
    TEST_EQUAL(mbedtls_psa_hash_update(&clone_middle, input2->x, input2->len),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_finish(&clone_middle,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected2->x, expected2->len, output, length);

    /* Nominal case with an operation cloned before finish */
    TEST_EQUAL(mbedtls_psa_hash_clone(&clone_end, &clone_more),
               PSA_SUCCESS);
    memset(output, 0, output_size);
    TEST_EQUAL(mbedtls_psa_hash_finish(&clone_end,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected2->x, expected2->len, output, length);
    mbedtls_free(output);
    output = NULL;

    /* Larger output buffer */
    TEST_EQUAL(mbedtls_psa_hash_clone(&clone_more, &clone_end),
               PSA_SUCCESS);
    output_size = expected2->len + 1;
    ASSERT_ALLOC(output, output_size);
    TEST_EQUAL(mbedtls_psa_hash_finish(&clone_end,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected2->x, expected2->len, output, length);
    mbedtls_free(output);
    output = NULL;

    /* We don't test with a smaller output buffer because this isn't
     * guaranteed to work: the core must pass a sufficiently large
     * output buffer to the driver. */

    /* Nominal case again after an error in a cloned operation */
    output_size = expected2->len;
    ASSERT_ALLOC(output, output_size);
    TEST_EQUAL(mbedtls_psa_hash_finish(&clone_more,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected2->x, expected2->len, output, length);
    mbedtls_free(output);
    output = NULL;

exit:
    mbedtls_free(output);
    mbedtls_psa_hash_abort(&operation0);
    mbedtls_psa_hash_abort(&clone_start);
    mbedtls_psa_hash_abort(&clone_middle);
    mbedtls_psa_hash_abort(&clone_end);
    mbedtls_psa_hash_abort(&clone_more);
}
/* END_CASE */

/* BEGIN_CASE */
void hash_empty(int alg_arg, data_t *expected)
{
    psa_algorithm_t alg = alg_arg;
    uint8_t *output = NULL;
    size_t output_size = expected->len;
    size_t length = SIZE_MAX;
    mbedtls_psa_hash_operation_t operation;
    memset(&operation, 0, sizeof(operation));

    ASSERT_ALLOC(output, output_size);

    /* One-shot */
    TEST_EQUAL(mbedtls_psa_hash_compute(alg, NULL, 0,
                                        output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected->x, expected->len, output, length);

    /* Multipart, no update */
    memset(output, 0, output_size);
    TEST_EQUAL(mbedtls_psa_hash_setup(&operation, alg),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_finish(&operation,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected->x, expected->len, output, length);

    /* Multipart, one update */
    memset(output, 0, output_size);
    memset(&operation, 0, sizeof(operation));
    TEST_EQUAL(mbedtls_psa_hash_setup(&operation, alg),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_update(&operation, NULL, 0),
               PSA_SUCCESS);
    TEST_EQUAL(mbedtls_psa_hash_finish(&operation,
                                       output, output_size, &length),
               PSA_SUCCESS);
    ASSERT_COMPARE(expected->x, expected->len, output, length);

exit:
    mbedtls_free(output);
    mbedtls_psa_hash_abort(&operation);
}
/* END_CASE */