/* SPDX-License-Identifier: BSD-2 */ /***********************************************************************; * Copyright (c) 2015 - 2018, Intel Corporation * All rights reserved. ***********************************************************************/ #include #include #include #include #include #include #include #include #include "tss2_tcti.h" #include "tss2_tcti_mssim.h" #include "tss2-tcti/tcti-common.h" #include "tss2-tcti/tcti-mssim.h" #include "util/key-value-parse.h" /* * This function is defined in the tcti-mssim module but not exposed through * the header. */ TSS2_RC mssim_kv_callback (const key_value_t *key_value, void *user_data); /* * This tests our ability to handle conf strings that have a port * component. In this case the 'conf_str_to_host_port' function * should set the 'port' parameter and so we check to be sure it's * set. */ static void conf_str_to_host_port_success_test (void **state) { TSS2_RC rc; char conf[] = "host=127.0.0.1,port=2321"; mssim_conf_t mssim_conf = { 0 }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (mssim_conf.port, 2321); assert_string_equal (mssim_conf.host, "127.0.0.1"); } /* * This tests our ability to handle conf strings that don't have the port * component of the URI. In this case the 'conf_str_to_host_port' function * should not touch the 'port' parameter and so we check to be sure it's * unchanged. */ #define NO_PORT_VALUE 646 static void conf_str_to_host_port_no_port_test (void **state) { TSS2_RC rc; char conf[] = "host=127.0.0.1"; mssim_conf_t mssim_conf = { .host = "foo", .port = NO_PORT_VALUE, }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal (mssim_conf.host, "127.0.0.1"); assert_int_equal (mssim_conf.port, NO_PORT_VALUE); } /* * This tests our ability to handle conf strings that have an IPv6 address * and port component. In this case the 'conf_str_to_host_port' function * should set the 'hostname' parameter and so we check to be sure it's * set without the [] brackets. */ static void conf_str_to_host_ipv6_port_success_test (void **state) { TSS2_RC rc; char conf[] = "host=::1,port=2321"; mssim_conf_t mssim_conf = { 0 }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (mssim_conf.port, 2321); assert_string_equal (mssim_conf.host, "::1"); } /* * This tests our ability to handle conf strings that have an IPv6 address * but no port component. In this case the 'conf_str_to_host_port' function * should not touch the 'port' parameter and so we check to be sure it's * unchanged. */ static void conf_str_to_host_ipv6_port_no_port_test (void **state) { TSS2_RC rc; char conf[] = "host=::1"; mssim_conf_t mssim_conf = { .port = NO_PORT_VALUE }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (mssim_conf.port, NO_PORT_VALUE); assert_string_equal (mssim_conf.host, "::1"); } /* * The 'conf_str_to_host_port' function rejects ports over UINT16_MAX. */ static void conf_str_to_host_port_invalid_port_large_test (void **state) { TSS2_RC rc; char conf[] = "host=127.0.0.1,port=99999"; mssim_conf_t mssim_conf = { 0 }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* The 'conf_str_to_host_port' function rejects URIs with port == 0 */ static void conf_str_to_host_port_invalid_port_0_test (void **state) { TSS2_RC rc; char conf[] = "host=127.0.0.1,port=0"; mssim_conf_t mssim_conf = { 0 }; rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* When passed all NULL values ensure that we get back the expected RC. */ static void tcti_socket_init_all_null_test (void **state) { TSS2_RC rc; rc = Tss2_Tcti_Mssim_Init (NULL, NULL, NULL); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Determine the size of a TCTI context structure. Requires calling the * initialization function for the device TCTI with the first parameter * (the TCTI context) NULL. */ static void tcti_socket_init_size_test (void **state) { size_t tcti_size = 0; TSS2_RC ret = TSS2_RC_SUCCESS; ret = Tss2_Tcti_Mssim_Init (NULL, &tcti_size, NULL); assert_int_equal (ret, TSS2_RC_SUCCESS); assert_int_equal (tcti_size, sizeof (TSS2_TCTI_MSSIM_CONTEXT)); } /* * Wrap the 'connect' system call. The mock queue for this function must have * an integer to return as a response. */ int __wrap_connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen) { return mock_type (int); } /* * Wrap the 'recv' system call. The mock queue for this function must have an * integer return value (the number of byts recv'd), as well as a pointer to * a buffer to copy data from to return to the caller. */ ssize_t __wrap_read (int sockfd, void *buf, size_t len) { ssize_t ret = mock_type (ssize_t); uint8_t *buf_in = mock_ptr_type (uint8_t*); memcpy (buf, buf_in, ret); return ret; } /* * Wrap the 'send' system call. The mock queue for this function must have an * integer to return as a response. */ ssize_t __wrap_write (int sockfd, const void *buf, size_t len) { return mock_type (TSS2_RC); } /* * This is a utility function used by other tests to setup a TCTI context. It * effectively wraps the init / allocate / init pattern as well as priming the * mock functions necessary for a the successful call to * 'Tss2_Tcti_Mssim_Init'. */ static TSS2_TCTI_CONTEXT* tcti_socket_init_from_conf (const char *conf) { size_t tcti_size = 0; uint8_t recv_buf[4] = { 0 }; TSS2_RC ret = TSS2_RC_SUCCESS; TSS2_TCTI_CONTEXT *ctx = NULL; printf ("%s: before first init\n", __func__); ret = Tss2_Tcti_Mssim_Init (NULL, &tcti_size, NULL); assert_true (ret == TSS2_RC_SUCCESS); ctx = calloc (1, tcti_size); assert_non_null (ctx); /* * two calls to connect, one for the data socket, one for the command * socket */ will_return (__wrap_connect, 0); will_return (__wrap_connect, 0); /* * two 'platform commands are sent on initialization, 4 bytes sent for * each, 4 byte response received (all 0's) for each. */ will_return (__wrap_write, 4); will_return (__wrap_read, 4); will_return (__wrap_read, recv_buf); will_return (__wrap_write, 4); will_return (__wrap_read, 4); will_return (__wrap_read, recv_buf); printf ("%s: before second_init\n", __func__); ret = Tss2_Tcti_Mssim_Init (ctx, &tcti_size, conf); printf ("%s: after second init\n", __func__); assert_int_equal (ret, TSS2_RC_SUCCESS); return ctx; } /* * This is a utility function to setup the "default" TCTI context. */ static int tcti_socket_setup (void **state) { printf ("%s: before tcti_socket_init_from_conf\n", __func__); *state = tcti_socket_init_from_conf ("host=127.0.0.1,port=666"); printf ("%s: done\n", __func__); return 0; } static void tcti_socket_init_null_conf_test (void **state) { TSS2_TCTI_CONTEXT *ctx = tcti_socket_init_from_conf (NULL); assert_non_null (ctx); free (ctx); } /* * This is a utility function to teardown a TCTI context allocated by the * tcti_socket_setup function. */ static int tcti_socket_teardown (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; Tss2_Tcti_Finalize (ctx); free (ctx); return 0; } /* * This test ensures that the GetPollHandles function in the device TCTI * returns the expected value. Since this TCTI does not support async I/O * on account of limitations in the kernel it just returns the * NOT_IMPLEMENTED response code. */ static void tcti_mssim_get_poll_handles_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; size_t num_handles = 5; TSS2_TCTI_POLL_HANDLE handles [5] = { 0 }; TSS2_RC rc; rc = Tss2_Tcti_GetPollHandles (ctx, handles, &num_handles); assert_int_equal (rc, TSS2_TCTI_RC_NOT_IMPLEMENTED); } /* */ static void tcti_socket_receive_null_size_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); TSS2_RC rc; /* Keep state machine check in `receive` from returning error. */ tcti_common->state = TCTI_STATE_RECEIVE; rc = Tss2_Tcti_Receive (ctx, NULL, /* NULL 'size' parameter */ NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); rc = Tss2_Tcti_Receive (ctx, NULL, /* NULL 'size' parameter */ (uint8_t*)1, /* non-NULL buffer */ TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); } /* * This test exercises the successful code path through the receive function. */ static void tcti_socket_receive_success_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); TSS2_RC rc = TSS2_RC_SUCCESS; size_t response_size = 0xc; uint8_t response_in [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, /* simulator appends 4 bytes of 0's to every response */ 0x00, 0x00, 0x00, 0x00 }; uint8_t response_out [12] = { 0 }; /* Keep state machine check in `receive` from returning error. */ tcti_common->state = TCTI_STATE_RECEIVE; /* receive response size */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [2]); /* receive tag */ will_return (__wrap_read, 2); will_return (__wrap_read, response_in); /* receive size (again) */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [2]); /* receive the rest of the command */ will_return (__wrap_read, 0xc - sizeof (TPM2_ST) - sizeof (UINT32)); will_return (__wrap_read, &response_in [6]); /* receive the 4 bytes of 0's appended by the simulator */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [12]); rc = Tss2_Tcti_Receive (ctx, &response_size, response_out, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_memory_equal (response_in, response_out, response_size); } /* */ static void tcti_socket_receive_size_success_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); TSS2_RC rc = TSS2_RC_SUCCESS; size_t response_size = 0; uint8_t response_in [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, /* simulator appends 4 bytes of 0's to every response */ 0x00, 0x00, 0x00, 0x00 }; uint8_t response_out [12] = { 0 }; /* Keep state machine check in `receive` from returning error. */ tcti_common->state = TCTI_STATE_RECEIVE; /* receive response size */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [2]); rc = Tss2_Tcti_Receive (ctx, &response_size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (response_size, 0xc); /* receive tag */ will_return (__wrap_read, 2); will_return (__wrap_read, response_in); /* receive size (again) */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [2]); /* receive the rest of the command */ will_return (__wrap_read, 0xc - sizeof (TPM2_ST) - sizeof (UINT32)); will_return (__wrap_read, &response_in [6]); /* receive the 4 bytes of 0's appended by the simulator */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [12]); rc = Tss2_Tcti_Receive (ctx, &response_size, response_out, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_memory_equal (response_in, response_out, response_size); } /* * This test causes the underlying 'read' call to return 0 / EOF when we * call the TCTI 'receive' function. In this case the TCTI should return an * IO error. */ static void tcti_mssim_receive_eof_first_read_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); TSS2_RC rc; /* output buffer for response */ uint8_t buf [TPM_HEADER_SIZE] = { 0 }; size_t size = sizeof (buf); /* Keep state machine check in `receive` from returning error. */ tcti_common->state = TCTI_STATE_RECEIVE; will_return (__wrap_read, 0); will_return (__wrap_read, buf); rc = Tss2_Tcti_Receive (ctx, &size, buf, TSS2_TCTI_TIMEOUT_BLOCK); assert_true (rc == TSS2_TCTI_RC_IO_ERROR); } /* * This test causes the underlying 'read' call to return EOF but only after * a successful read that gets us the response size. This results in the * an IO_ERROR RC being returned. */ static void tcti_mssim_receive_eof_second_read_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); TSS2_RC rc; /* input response buffer */ uint8_t response_in [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, /* simulator appends 4 bytes of 0's to every response */ 0x00, 0x00, 0x00, 0x00 }; /* output response buffer */ uint8_t response_out [12] = { 0 }; size_t size = sizeof (response_out); /* Keep state machine check in `receive` from returning error. */ tcti_common->state = TCTI_STATE_RECEIVE; /* setup response size for first read */ will_return (__wrap_read, 4); will_return (__wrap_read, &response_in [2]); /* setup 0 for EOF on second read */ will_return (__wrap_read, 0); will_return (__wrap_read, response_in); rc = Tss2_Tcti_Receive (ctx, &size, response_out, TSS2_TCTI_TIMEOUT_BLOCK); assert_true (rc == TSS2_TCTI_RC_IO_ERROR); } /* * This test exercises the successful code path through the transmit function. */ static void tcti_socket_transmit_success_test (void **state) { TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_RC rc = TSS2_RC_SUCCESS; uint8_t command [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02 }; size_t command_size = sizeof (command); /* send the TPM2_SEND_COMMAND code */ will_return (__wrap_write, 4); /* send the locality for the command */ will_return (__wrap_write, 1); /* send the number of bytes in command */ will_return (__wrap_write, 4); /* send the command buffer */ will_return (__wrap_write, 0xc); rc = Tss2_Tcti_Transmit (ctx, command_size, command); assert_int_equal (rc, TSS2_RC_SUCCESS); } int main (int argc, char *argv[]) { const struct CMUnitTest tests[] = { cmocka_unit_test (conf_str_to_host_port_success_test), cmocka_unit_test (conf_str_to_host_port_no_port_test), cmocka_unit_test (conf_str_to_host_ipv6_port_success_test), cmocka_unit_test (conf_str_to_host_ipv6_port_no_port_test), cmocka_unit_test (conf_str_to_host_port_invalid_port_large_test), cmocka_unit_test (conf_str_to_host_port_invalid_port_0_test), cmocka_unit_test (tcti_socket_init_all_null_test), cmocka_unit_test (tcti_socket_init_size_test), cmocka_unit_test (tcti_socket_init_null_conf_test), cmocka_unit_test_setup_teardown (tcti_mssim_get_poll_handles_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_socket_receive_null_size_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_socket_receive_success_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_socket_receive_size_success_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_mssim_receive_eof_first_read_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_mssim_receive_eof_second_read_test, tcti_socket_setup, tcti_socket_teardown), cmocka_unit_test_setup_teardown (tcti_socket_transmit_success_test, tcti_socket_setup, tcti_socket_teardown) }; return cmocka_run_group_tests (tests, NULL, NULL); }