Add mbedtls_test_buffer to SSL unit tests
In a unit test we want to avoid accessing the network. To test the
handshake in the unit test suite we need to implement a connection
between the server and the client. This ring buffer implementation will
serve as the said connection.
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 2fa716b..ea81a8b 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -2,6 +2,141 @@
#include <mbedtls/ssl.h>
#include <mbedtls/ssl_internal.h>
+
+/*
+ * Buffer structure for custom I/O callbacks.
+ */
+
+typedef struct mbedtls_test_buffer
+{
+ size_t start;
+ size_t content_length;
+ size_t capacity;
+ unsigned char *buffer;
+} mbedtls_test_buffer;
+
+/*
+ * Initialises \p buf. After calling this function it is safe to call
+ * `mbedtls_test_buffer_free()` on \p buf.
+ */
+void mbedtls_test_buffer_init( mbedtls_test_buffer *buf )
+{
+ memset( buf, 0, sizeof( *buf ) );
+}
+
+/*
+ * Sets up \p buf. After calling this function it is safe to call
+ * `mbedtls_test_buffer_put()` and `mbedtls_test_buffer_get()` on \p buf.
+ */
+int mbedtls_test_buffer_setup( mbedtls_test_buffer *buf, size_t capacity )
+{
+ buf->buffer = (unsigned char*) mbedtls_calloc( capacity,
+ sizeof(unsigned char) );
+ if( NULL == buf->buffer )
+ return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+ buf->capacity = capacity;
+
+ return 0;
+}
+
+void mbedtls_test_buffer_free( mbedtls_test_buffer *buf )
+{
+ if( buf->buffer != NULL )
+ mbedtls_free( buf->buffer );
+
+ memset( buf, 0, sizeof( *buf ) );
+}
+
+/*
+ * Puts \p input_len bytes from the \p input buffer into the ring buffer \p buf.
+ *
+ * \p buf must have been initialized and set up by calling
+ * `mbedtls_test_buffer_init()` and `mbedtls_test_buffer_setup()`.
+ *
+ * \retval \p input_len, if the data fits.
+ * \retval 0 <= value < \p input_len, if the data does not fit.
+ * \retval -1, if \p buf is NULL, it hasn't been set up or \p input_len is not
+ * zero and \p input is NULL.
+ */
+int mbedtls_test_buffer_put( mbedtls_test_buffer *buf,
+ const unsigned char* input, size_t input_len )
+{
+ size_t overflow = 0;
+
+ if( ( buf == NULL ) || ( buf->buffer == NULL ) )
+ return -1;
+
+ /* Reduce input_len to a number that fits in the buffer. */
+ if ( ( buf->content_length + input_len ) > buf->capacity )
+ {
+ input_len = buf->capacity - buf->content_length;
+ }
+
+ if( input == NULL )
+ {
+ return ( input_len == 0 ) ? 0 : -1;
+ }
+
+ /* Calculate the number of bytes that need to be placed at lower memory
+ * address */
+ if( buf->start + buf->content_length + input_len
+ > buf->capacity )
+ {
+ overflow = ( buf->start + buf->content_length + input_len )
+ % buf->capacity;
+ }
+
+ memcpy( buf->buffer + buf->start + buf->content_length, input,
+ input_len - overflow );
+ memcpy( buf->buffer, input + input_len - overflow, overflow );
+ buf->content_length += input_len;
+
+ return input_len;
+}
+
+/*
+ * Gets \p output_len bytes from the \p output buffer into the ring buffer
+ * \p buf.
+ *
+ * \p buf must have been initialized and set up by calling
+ * `mbedtls_test_buffer_init()` and `mbedtls_test_buffer_setup()`.
+ *
+ * \retval \p output_len, if the data is available.
+ * \retval 0 <= value < \p output_len, if the data is not available.
+ * \retval -1, if \buf is NULL, it hasn't been set up or \p output_len is not
+ * zero and \p output is NULL
+ */
+int mbedtls_test_buffer_get( mbedtls_test_buffer *buf,
+ unsigned char* output, size_t output_len )
+{
+ size_t overflow = 0;
+
+ if( ( buf == NULL ) || ( buf->buffer == NULL ) )
+ return -1;
+
+ if( output == NULL )
+ {
+ return ( output_len == 0 ) ? 0 : -1;
+ }
+
+ if( buf->content_length < output_len )
+ output_len = buf->content_length;
+
+ /* Calculate the number of bytes that need to be drawn from lower memory
+ * address */
+ if( buf->start + output_len > buf->capacity )
+ {
+ overflow = ( buf->start + output_len ) % buf->capacity;
+ }
+
+ memcpy( output, buf->buffer + buf->start, output_len - overflow );
+ memcpy( output + output_len - overflow, buf->buffer, overflow );
+ buf->content_length -= output_len;
+ buf->start = ( buf->start + output_len ) % buf->capacity;
+
+ return output_len;
+}
+
/*
* Helper function setting up inverse record transformations
* using given cipher, hash, EtM mode, authentication tag length,
@@ -361,6 +496,175 @@
* END_DEPENDENCIES
*/
+/* BEGIN_CASE */
+void test_callback_buffer_sanity()
+{
+ enum { MSGLEN = 10 };
+ mbedtls_test_buffer buf;
+ unsigned char input[MSGLEN];
+ unsigned char output[MSGLEN];
+
+ memset( input, 0, sizeof(input) );
+
+ /* Make sure calling put and get on NULL buffer results in error. */
+ TEST_ASSERT( mbedtls_test_buffer_put( NULL, input, sizeof( input ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( NULL, output, sizeof( output ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_put( NULL, NULL, sizeof( input ) ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( NULL, NULL, sizeof( output ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_put( NULL, NULL, 0 ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( NULL, NULL, 0 ) == -1 );
+
+ /* Make sure calling put and get on a buffer that hasn't been set up results
+ * in eror. */
+ mbedtls_test_buffer_init( &buf );
+
+ TEST_ASSERT( mbedtls_test_buffer_put( &buf, input, sizeof( input ) ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( &buf, output, sizeof( output ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_put( &buf, NULL, sizeof( input ) ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( &buf, NULL, sizeof( output ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_put( &buf, NULL, 0 ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( &buf, NULL, 0 ) == -1 );
+
+ /* Make sure calling put end get on NULL input and output only results in
+ * error if the length is not zero. */
+
+ TEST_ASSERT( mbedtls_test_buffer_setup( &buf, sizeof( input ) ) == 0 );
+
+ TEST_ASSERT( mbedtls_test_buffer_put( &buf, NULL, sizeof( input ) ) == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_get( &buf, NULL, sizeof( output ) )
+ == -1 );
+ TEST_ASSERT( mbedtls_test_buffer_put( &buf, NULL, 0 ) == 0 );
+ TEST_ASSERT( mbedtls_test_buffer_get( &buf, NULL, 0 ) == 0 );
+
+exit:
+
+ mbedtls_test_buffer_free( &buf );
+}
+/* END_CASE */
+
+/*
+ * Test if the implementation of `mbedtls_test_buffer` related functions is
+ * correct and works as expected.
+ *
+ * That is
+ * - If we try to put in \p put1 bytes then we can put in \p put1_ret bytes.
+ * - Afterwards if we try to get \p get1 bytes then we can get \get1_ret bytes.
+ * - Next, if we try to put in \p put1 bytes then we can put in \p put1_ret
+ * bytes.
+ * - Afterwards if we try to get \p get1 bytes then we can get \get1_ret bytes.
+ * - All of the bytes we got match the bytes we put in in a FIFO manner.
+ */
+
+/* BEGIN_CASE */
+void test_callback_buffer( int size, int put1, int put1_ret,
+ int get1, int get1_ret, int put2, int put2_ret,
+ int get2, int get2_ret )
+{
+ enum { ROUNDS = 2 };
+ size_t put[ROUNDS];
+ int put_ret[ROUNDS];
+ size_t get[ROUNDS];
+ int get_ret[ROUNDS];
+ mbedtls_test_buffer buf;
+ unsigned char* input = NULL;
+ size_t input_len;
+ unsigned char* output = NULL;
+ size_t output_len;
+ size_t i, written, read;
+ int j;
+
+ mbedtls_test_buffer_init( &buf );
+ TEST_ASSERT( mbedtls_test_buffer_setup( &buf, size ) == 0 );
+
+ /* Check the sanity of input parameters and initialise local variables. That
+ * is, ensure that the amount of data is not negative and that we are not
+ * expecting more to put or get than we actually asked for. */
+ TEST_ASSERT( put1 >= 0 );
+ put[0] = put1;
+ put_ret[0] = put1_ret;
+ TEST_ASSERT( put1_ret <= put1 );
+ TEST_ASSERT( put2 >= 0 );
+ put[1] = put2;
+ put_ret[1] = put2_ret;
+ TEST_ASSERT( put2_ret <= put2 );
+
+ TEST_ASSERT( get1 >= 0 );
+ get[0] = get1;
+ get_ret[0] = get1_ret;
+ TEST_ASSERT( get1_ret <= get1 );
+ TEST_ASSERT( get2 >= 0 );
+ get[1] = get2;
+ get_ret[1] = get2_ret;
+ TEST_ASSERT( get2_ret <= get2 );
+
+ input_len = 0;
+ /* Calculate actual input and output lengths */
+ for( j = 0; j < ROUNDS; j++ )
+ {
+ if( put_ret[j] > 0 )
+ {
+ input_len += put_ret[j];
+ }
+ }
+ /* In order to always have a valid pointer we always allocate at least 1
+ * byte. */
+ if( input_len == 0 )
+ input_len = 1;
+ ASSERT_ALLOC( input, input_len );
+
+ output_len = 0;
+ for( j = 0; j < ROUNDS; j++ )
+ {
+ if( get_ret[j] > 0 )
+ {
+ output_len += get_ret[j];
+ }
+ }
+ TEST_ASSERT( output_len <= input_len );
+ /* In order to always have a valid pointer we always allocate at least 1
+ * byte. */
+ if( output_len == 0 )
+ output_len = 1;
+ ASSERT_ALLOC( output, output_len );
+
+ /* Fill up the buffer with structured data so that unwanted changes
+ * can be detected */
+ for( i = 0; i < input_len; i++ )
+ {
+ input[i] = i & 0xFF;
+ }
+
+ written = read = 0;
+ for( j = 0; j < ROUNDS; j++ )
+ {
+ TEST_ASSERT( put_ret[j] == mbedtls_test_buffer_put( &buf,
+ input + written, put[j] ) );
+ written += put_ret[j];
+ TEST_ASSERT( get_ret[j] == mbedtls_test_buffer_get( &buf,
+ output + read, get[j] ) );
+ read += get_ret[j];
+ TEST_ASSERT( read <= written );
+ if( get_ret[j] > 0 )
+ {
+ TEST_ASSERT( memcmp( output + read - get_ret[j],
+ input + read - get_ret[j], get_ret[j] )
+ == 0 );
+ }
+ }
+
+exit:
+
+ mbedtls_free( input );
+ mbedtls_free( output );
+ mbedtls_test_buffer_free( &buf );
+}
+/* END_CASE */
+
/* BEGIN_CASE depends_on:MBEDTLS_SSL_DTLS_ANTI_REPLAY */
void ssl_dtls_replay( data_t * prevs, data_t * new, int ret )
{