/*
 *  Common code library for SSL test programs.
 *
 *  In addition to the functions in this file, there is shared source code
 *  that cannot be compiled separately in "ssl_test_common_source.c".
 *
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may
 *  not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "ssl_test_lib.h"

#if defined(MBEDTLS_TEST_HOOKS)
#include "test/helpers.h"
#endif

#if !defined(MBEDTLS_SSL_TEST_IMPOSSIBLE)

void my_debug( void *ctx, int level,
               const char *file, int line,
               const char *str )
{
    const char *p, *basename;

    /* Extract basename from file */
    for( p = basename = file; *p != '\0'; p++ )
        if( *p == '/' || *p == '\\' )
            basename = p + 1;

    mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s",
                     basename, line, level, str );
    fflush( (FILE *) ctx  );
}

mbedtls_time_t dummy_constant_time( mbedtls_time_t* time )
{
    (void) time;
    return 0x5af2a056;
}

#if !defined(MBEDTLS_TEST_USE_PSA_CRYPTO_RNG)
static int dummy_entropy( void *data, unsigned char *output, size_t len )
{
    size_t i;
    int ret;
    (void) data;

    ret = mbedtls_entropy_func( data, output, len );
    for( i = 0; i < len; i++ )
    {
        //replace result with pseudo random
        output[i] = (unsigned char) rand();
    }
    return( ret );
}
#endif

void rng_init( rng_context_t *rng )
{
#if defined(MBEDTLS_TEST_USE_PSA_CRYPTO_RNG)
    (void) rng;
    psa_crypto_init( );
#else /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */

#if defined(MBEDTLS_CTR_DRBG_C)
    mbedtls_ctr_drbg_init( &rng->drbg );
#elif defined(MBEDTLS_HMAC_DRBG_C)
    mbedtls_hmac_drbg_init( &rng->drbg );
#else
#error "No DRBG available"
#endif

    mbedtls_entropy_init( &rng->entropy );
#endif /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */
}

int rng_seed( rng_context_t *rng, int reproducible, const char *pers )
{
#if defined(MBEDTLS_USE_PSA_CRYPTO)
    if( reproducible )
    {
        mbedtls_fprintf( stderr,
                         "MBEDTLS_USE_PSA_CRYPTO does not support reproducible mode.\n" );
        return( -1 );
    }
#endif
#if defined(MBEDTLS_TEST_USE_PSA_CRYPTO_RNG)
    /* The PSA crypto RNG does its own seeding. */
    (void) rng;
    (void) pers;
    if( reproducible )
    {
        mbedtls_fprintf( stderr,
                         "The PSA RNG does not support reproducible mode.\n" );
        return( -1 );
    }
    return( 0 );
#else /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */
    int ( *f_entropy )( void *, unsigned char *, size_t ) =
        ( reproducible ? dummy_entropy : mbedtls_entropy_func );

    if ( reproducible )
        srand( 1 );

#if defined(MBEDTLS_CTR_DRBG_C)
    int ret = mbedtls_ctr_drbg_seed( &rng->drbg,
                                     f_entropy, &rng->entropy,
                                     (const unsigned char *) pers,
                                     strlen( pers ) );
#elif defined(MBEDTLS_HMAC_DRBG_C)
#if defined(MBEDTLS_SHA256_C)
    const mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
#elif defined(MBEDTLS_SHA512_C)
    const mbedtls_md_type_t md_type = MBEDTLS_MD_SHA512;
#else
#error "No message digest available for HMAC_DRBG"
#endif
    int ret = mbedtls_hmac_drbg_seed( &rng->drbg,
                                      mbedtls_md_info_from_type( md_type ),
                                      f_entropy, &rng->entropy,
                                      (const unsigned char *) pers,
                                      strlen( pers ) );
#else /* !defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_HMAC_DRBG_C) */
#error "No DRBG available"
#endif /* !defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_HMAC_DRBG_C) */

    if( ret != 0 )
    {
        mbedtls_printf( " failed\n  ! mbedtls_ctr_drbg_seed returned -0x%x\n",
                        (unsigned int) -ret );
        return( ret );
    }
#endif /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */

    return( 0 );
}

void rng_free( rng_context_t *rng )
{
#if defined(MBEDTLS_TEST_USE_PSA_CRYPTO_RNG)
    (void) rng;
    /* Deinitialize the PSA crypto subsystem. This deactivates all PSA APIs.
     * This is ok because none of our applications try to do any crypto after
     * deinitializing the RNG. */
    mbedtls_psa_crypto_free( );
#else /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */

#if defined(MBEDTLS_CTR_DRBG_C)
    mbedtls_ctr_drbg_free( &rng->drbg );
#elif defined(MBEDTLS_HMAC_DRBG_C)
    mbedtls_hmac_drbg_free( &rng->drbg );
#else
#error "No DRBG available"
#endif

    mbedtls_entropy_free( &rng->entropy );
#endif /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */
}

int rng_get( void *p_rng, unsigned char *output, size_t output_len )
{
#if defined(MBEDTLS_TEST_USE_PSA_CRYPTO_RNG)
    (void) p_rng;
    return( mbedtls_psa_get_random( MBEDTLS_PSA_RANDOM_STATE,
                                    output, output_len ) );
#else /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */
    rng_context_t *rng = p_rng;

#if defined(MBEDTLS_CTR_DRBG_C)
    return( mbedtls_ctr_drbg_random( &rng->drbg, output, output_len ) );
#elif defined(MBEDTLS_HMAC_DRBG_C)
    return( mbedtls_hmac_drbg_random( &rng->drbg, output, output_len ) );
#else
#error "No DRBG available"
#endif

#endif /* !MBEDTLS_TEST_USE_PSA_CRYPTO_RNG */
}

#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK)
int ca_callback( void *data, mbedtls_x509_crt const *child,
                 mbedtls_x509_crt **candidates )
{
    int ret = 0;
    mbedtls_x509_crt *ca = (mbedtls_x509_crt *) data;
    mbedtls_x509_crt *first;

    /* This is a test-only implementation of the CA callback
     * which always returns the entire list of trusted certificates.
     * Production implementations managing a large number of CAs
     * should use an efficient presentation and lookup for the
     * set of trusted certificates (such as a hashtable) and only
     * return those trusted certificates which satisfy basic
     * parental checks, such as the matching of child `Issuer`
     * and parent `Subject` field or matching key identifiers. */
    ((void) child);

    first = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
    if( first == NULL )
    {
        ret = -1;
        goto exit;
    }
    mbedtls_x509_crt_init( first );

    if( mbedtls_x509_crt_parse_der( first, ca->raw.p, ca->raw.len ) != 0 )
    {
        ret = -1;
        goto exit;
    }

    while( ca->next != NULL )
    {
        ca = ca->next;
        if( mbedtls_x509_crt_parse_der( first, ca->raw.p, ca->raw.len ) != 0 )
        {
            ret = -1;
            goto exit;
        }
    }

exit:

    if( ret != 0 )
    {
        mbedtls_x509_crt_free( first );
        mbedtls_free( first );
        first = NULL;
    }

    *candidates = first;
    return( ret );
}
#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */

int delayed_recv( void *ctx, unsigned char *buf, size_t len )
{
    static int first_try = 1;
    int ret;

    if( first_try )
    {
        first_try = 0;
        return( MBEDTLS_ERR_SSL_WANT_READ );
    }

    ret = mbedtls_net_recv( ctx, buf, len );
    if( ret != MBEDTLS_ERR_SSL_WANT_READ )
        first_try = 1; /* Next call will be a new operation */
    return( ret );
}

int delayed_send( void *ctx, const unsigned char *buf, size_t len )
{
    static int first_try = 1;
    int ret;

    if( first_try )
    {
        first_try = 0;
        return( MBEDTLS_ERR_SSL_WANT_WRITE );
    }

    ret = mbedtls_net_send( ctx, buf, len );
    if( ret != MBEDTLS_ERR_SSL_WANT_WRITE )
        first_try = 1; /* Next call will be a new operation */
    return( ret );
}

#if !defined(MBEDTLS_TIMING_C)
int idle( mbedtls_net_context *fd,
          int idle_reason )
#else
int idle( mbedtls_net_context *fd,
          mbedtls_timing_delay_context *timer,
          int idle_reason )
#endif
{
    int ret;
    int poll_type = 0;

    if( idle_reason == MBEDTLS_ERR_SSL_WANT_WRITE )
        poll_type = MBEDTLS_NET_POLL_WRITE;
    else if( idle_reason == MBEDTLS_ERR_SSL_WANT_READ )
        poll_type = MBEDTLS_NET_POLL_READ;
#if !defined(MBEDTLS_TIMING_C)
    else
        return( 0 );
#endif

    while( 1 )
    {
        /* Check if timer has expired */
#if defined(MBEDTLS_TIMING_C)
        if( timer != NULL &&
            mbedtls_timing_get_delay( timer ) == 2 )
        {
            break;
        }
#endif /* MBEDTLS_TIMING_C */

        /* Check if underlying transport became available */
        if( poll_type != 0 )
        {
            ret = mbedtls_net_poll( fd, poll_type, 0 );
            if( ret < 0 )
                return( ret );
            if( ret == poll_type )
                break;
        }
    }

    return( 0 );
}

#if defined(MBEDTLS_TEST_HOOKS)

void test_hooks_init( void )
{
    mbedtls_test_info_reset( );

#if defined(MBEDTLS_TEST_MUTEX_USAGE)
    mbedtls_test_mutex_usage_init( );
#endif
}

int test_hooks_failure_detected( void )
{
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
    /* Errors are reported via mbedtls_test_info. */
    mbedtls_test_mutex_usage_check( );
#endif

    if( mbedtls_test_info.result != MBEDTLS_TEST_RESULT_SUCCESS )
        return( 1 );
    return( 0 );
}

void test_hooks_free( void )
{
}

#endif /* MBEDTLS_TEST_HOOKS */

#endif /* !defined(MBEDTLS_SSL_TEST_IMPOSSIBLE) */
