diff options
Diffstat (limited to 'core/arch/arm/pta/se_api_self_tests.c')
-rw-r--r-- | core/arch/arm/pta/se_api_self_tests.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/core/arch/arm/pta/se_api_self_tests.c b/core/arch/arm/pta/se_api_self_tests.c new file mode 100644 index 0000000..f6f5ec4 --- /dev/null +++ b/core/arch/arm/pta/se_api_self_tests.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <compiler.h> +#include <kernel/pseudo_ta.h> +#include <tee_api_types.h> +#include <tee_api_defines.h> +#include <trace.h> + +#include <tee/se/manager.h> +#include <tee/se/reader.h> +#include <tee/se/session.h> +#include <tee/se/iso7816.h> +#include <tee/se/aid.h> +#include <tee/se/apdu.h> +#include <tee/se/channel.h> +#include <tee/se/util.h> + +#include <stdlib.h> +#include <string.h> + +#include "aid_priv.h" +#include "apdu_priv.h" +#include "reader_priv.h" + + +#define TA_NAME "se_api_self_tests.ta" + +#define MAX_READERS 10 + +#define CMD_SELF_TESTS 0 + +#define SE_API_SELF_TEST_UUID \ + { 0xAEB79790, 0x6F03, 0x11E4, \ + { 0x98, 0x03, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66 } } + +#define ASSERT(expr) \ + do { \ + if (!(expr)) { \ + EMSG("assertion '%s' failed at %s:%d (func '%s')", \ + #expr, __FILE__, __LINE__, __func__); \ + return TEE_ERROR_GENERIC; \ + } \ + } while (0) + +#define CHECK(ret) \ + do { \ + if (ret != TEE_SUCCESS) \ + return ret; \ + } while (0) + +/* + * Trusted Application Entry Points + */ + +static TEE_Result test_reader(struct tee_se_reader_proxy **handle) +{ + TEE_Result ret; + uint8_t cmd[] = { ISO7816_CLA, MANAGE_CHANNEL_CMD, + OPEN_CHANNEL, OPEN_NEXT_AVAILABLE }; + uint8_t resp[3]; + size_t resp_size = sizeof(resp); + const int expected_channel_id = 1; + + DMSG("entry"); + /* transmit should fail since no one attached to the reader */ + ret = tee_se_reader_transmit(handle[0], cmd, sizeof(cmd), + resp, &resp_size); + ASSERT(ret == TEE_ERROR_BAD_STATE); + + ret = tee_se_reader_attach(handle[0]); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_reader_attach(handle[0]); + ASSERT(ret == TEE_SUCCESS); + + /* referenced by 2 owners */ + ASSERT(2 == tee_se_reader_get_refcnt(handle[0])); + + ret = tee_se_reader_transmit(handle[0], cmd, sizeof(cmd), + resp, &resp_size); + ASSERT(ret == TEE_SUCCESS); + ASSERT(resp[0] == expected_channel_id && + resp[1] == CMD_OK_SW1 && resp[2] == CMD_OK_SW2); + + tee_se_reader_detach(handle[0]); + + ASSERT(1 == tee_se_reader_get_refcnt(handle[0])); + + tee_se_reader_detach(handle[0]); + DMSG("exit"); + + return TEE_SUCCESS; +} + +static TEE_Result test_aid(struct tee_se_reader_proxy **proxies) +{ + struct tee_se_session *s = NULL; + struct tee_se_channel *b = NULL, *l = NULL; + struct tee_se_aid *aid = NULL; + TEE_Result ret; + + DMSG("entry"); + ret = tee_se_aid_create("D0000CAFE00001", &aid); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_reader_open_session(proxies[0], &s); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_session_open_basic_channel(s, aid, &b); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_session_open_logical_channel(s, aid, &l); + ASSERT(ret == TEE_SUCCESS); + + ASSERT(tee_se_aid_get_refcnt(aid) == 3); + + tee_se_session_close_channel(s, b); + tee_se_session_close_channel(s, l); + + ASSERT(tee_se_aid_get_refcnt(aid) == 1); + + tee_se_session_close(s); + tee_se_aid_release(aid); + DMSG("exit"); + + return TEE_SUCCESS; +} + +static TEE_Result test_session(struct tee_se_reader_proxy **proxies) +{ + struct tee_se_channel *c1 = NULL, *c2 = NULL; + struct tee_se_session *s1 = NULL, *s2 = NULL; + TEE_Result ret; + + DMSG("entry"); + ret = tee_se_reader_open_session(proxies[0], &s1); + ASSERT(ret == TEE_SUCCESS); + + /* should success, multiple sessions open by different user */ + ret = tee_se_reader_open_session(proxies[0], &s2); + ASSERT(ret == TEE_SUCCESS); + + /* open basic channel on s1 (should success) */ + ret = tee_se_session_open_basic_channel(s1, NULL, &c1); + ASSERT(ret == TEE_SUCCESS); + + /* open basic channel on s2 + * (should fail, basic channel is locked by s1) + */ + ret = tee_se_session_open_basic_channel(s2, NULL, &c2); + ASSERT(ret == TEE_ERROR_NOT_SUPPORTED); + ASSERT(c2 == NULL); + + /* close basic channel on s1 */ + tee_se_session_close_channel(s1, c1); + c1 = NULL; + + /* open basic channel on s2 (this time should success) */ + ret = tee_se_session_open_basic_channel(s1, NULL, &c2); + ASSERT(ret == TEE_SUCCESS); + + /* close basic channel on s2 */ + tee_se_session_close_channel(s2, c2); + c2 = NULL; + + /* open logical channel on s1 and s2 (both should success) */ + ret = tee_se_session_open_logical_channel(s1, NULL, &c1); + ASSERT(ret == TEE_SUCCESS); + ret = tee_se_session_open_logical_channel(s2, NULL, &c2); + ASSERT(ret == TEE_SUCCESS); + + /* clean up */ + tee_se_session_close_channel(s1, c1); + tee_se_session_close_channel(s2, c2); + + tee_se_session_close(s1); + tee_se_session_close(s2); + DMSG("exit"); + + return TEE_SUCCESS; +} + +static TEE_Result test_select_resp(struct tee_se_reader_proxy **proxies) +{ + struct tee_se_aid *aid = NULL; + struct tee_se_session *s = NULL; + struct tee_se_channel *c = NULL; + struct resp_apdu *resp; + TEE_Result ret; + + DMSG("entry"); + ret = tee_se_aid_create("D0000CAFE00001", &aid); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_reader_open_session(proxies[0], &s); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_session_open_logical_channel(s, aid, &c); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_channel_get_select_response(c, &resp); + ASSERT(ret == TEE_SUCCESS); + + ASSERT((resp_apdu_get_sw1(resp) == CMD_OK_SW1) && + (resp_apdu_get_sw2(resp) == CMD_OK_SW2)); + + /* + * the ownership of resp apdu should be the channel + * and it should be the only owner + */ + ASSERT(apdu_get_refcnt(to_apdu_base(resp)) == 1); + + /* increase the reference counter of resp apdu */ + apdu_acquire(to_apdu_base(resp)); + + /* clean up */ + tee_se_session_close_channel(s, c); + + /* channel should release resp apdu when closed */ + ASSERT(apdu_get_refcnt(to_apdu_base(resp)) == 1); + apdu_release(to_apdu_base(resp)); + + tee_se_session_close(s); + tee_se_aid_release(aid); + DMSG("exit"); + + return TEE_SUCCESS; +} + +/* + * The JAVA Card Simulator (jcardsim.jar) built-in applet(s): + * + * AID |Type + * -------------------------------------+---------------------- + * D0000CAFE00001 | MultiSelectable + * (default selected on basic channel) | + * -------------------------------------+---------------------- + * D0000CAFE00002 | Non-MultiSelectable + * -------------------------------------+---------------------- + * + */ +static TEE_Result test_logical_channel(struct tee_se_reader_proxy **proxies) +{ + struct tee_se_channel *channel[MAX_LOGICAL_CHANNEL] = { NULL }; + struct tee_se_aid *aid = NULL; + struct tee_se_session *s = NULL; + TEE_Result ret; + int i; + + DMSG("entry"); + ret = tee_se_reader_open_session(proxies[0], &s); + ASSERT(ret == TEE_SUCCESS); + + /* + * test open logical channels based on AID selected on basic channel + * (D0000CAFE00001 is default selected on basic channel, + * this call should success since D0000CAFE00001 is MultiSelectable, + * upon open, each logical channel should select D0000CAFE00001) + */ + for (i = 1; i < MAX_LOGICAL_CHANNEL; i ++) { + ret = tee_se_session_open_logical_channel(s, NULL, &channel[i]); + ASSERT(ret == TEE_SUCCESS); + } + + /* + * should fail on next open + * (exceeds maximum logical channel number) + */ + ret = tee_se_session_open_logical_channel(s, NULL, &channel[0]); + ASSERT(ret == TEE_ERROR_NOT_SUPPORTED); + + /* close 3 channels */ + for (i = 1; i < 4; i++) { + tee_se_session_close_channel(s, channel[i]); + channel[i] = NULL; + } + + /* re-open 3 channels (should success) */ + for (i = 1; i < 4; i++) { + ret = tee_se_session_open_logical_channel(s, NULL, &channel[i]); + ASSERT(ret == TEE_SUCCESS); + } + + /* logical channel 1 select D0000CAFE00002 (should success) */ + tee_se_aid_create("D0000CAFE00002", &aid); + ret = tee_se_channel_select(channel[1], aid); + ASSERT(ret == TEE_SUCCESS); + + /* logical channel 2 select D0000CAFE00002 + * (should fail since D0000CAFE00002 is not MultiSelectable) + */ + ret = tee_se_channel_select(channel[2], aid); + ASSERT(ret == TEE_ERROR_NOT_SUPPORTED); + + /* clean up */ + for (i = 1; i < MAX_LOGICAL_CHANNEL; i++) + tee_se_session_close_channel(s, channel[i]); + tee_se_session_close(s); + tee_se_aid_release(aid); + DMSG("exit"); + + return TEE_SUCCESS; +} + +static TEE_Result verify_result(struct resp_apdu *apdu, const char *data) +{ + size_t str_length = strlen(data); + size_t byte_length = strlen(data) / 2; + uint8_t *resp_data = resp_apdu_get_data(apdu); + size_t resp_len = resp_apdu_get_data_len(apdu); + uint8_t bytes[byte_length]; + size_t i = 0; + + ASSERT(resp_len == byte_length); + + hex_decode(data, str_length, bytes); + while (i < resp_len) { + ASSERT(bytes[i] == resp_data[i]); + i++; + } + return TEE_SUCCESS; +} + +static TEE_Result test_transmit(struct tee_se_reader_proxy **proxies) +{ + struct tee_se_channel *c1 = NULL, *c2 = NULL; + struct tee_se_session *s1 = NULL, *s2 = NULL; + struct tee_se_aid *full_aid = NULL, *partial_aid = NULL; + struct cmd_apdu *cmd; + struct resp_apdu *resp; + size_t tx_buf_len = 0, rx_buf_len = 7; + TEE_Result ret; + + DMSG("entry"); + ret = tee_se_aid_create("D0000CAFE00001", &full_aid); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_aid_create("D0000CAFE0000", &partial_aid); + ASSERT(ret == TEE_SUCCESS); + + cmd = alloc_cmd_apdu(ISO7816_CLA, 0xFF, 0x0, 0x0, + tx_buf_len, rx_buf_len, NULL); + ASSERT(cmd); + resp = alloc_resp_apdu(rx_buf_len); + ASSERT(resp); + + ret = tee_se_reader_open_session(proxies[0], &s1); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_reader_open_session(proxies[0], &s2); + ASSERT(ret == TEE_SUCCESS); + + /* open logical channel on s1 (given full aid) */ + ret = tee_se_session_open_logical_channel(s1, full_aid, &c1); + ASSERT(ret == TEE_SUCCESS); + + /* should route to D0000CAFE00001 */ + ret = tee_se_channel_transmit(c1, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + + /* select next should fail (full aid given) */ + ret = tee_se_channel_select_next(c1); + ASSERT(ret == TEE_ERROR_ITEM_NOT_FOUND); + + /* open logical channel on s2 (given partial aid) */ + ret = tee_se_session_open_logical_channel(s2, partial_aid, &c2); + ASSERT(ret == TEE_SUCCESS); + + /* should route to D0000CAFE00001 */ + ret = tee_se_channel_transmit(c2, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + ret = verify_result(resp, "D0000CAFE00001"); + ASSERT(ret == TEE_SUCCESS); + + /* select next should success (select D0000CAFE00002) */ + ret = tee_se_channel_select_next(c2); + ASSERT(ret == TEE_SUCCESS); + + /* should route to D0000CAFE00002 */ + ret = tee_se_channel_transmit(c2, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + ret = verify_result(resp, "D0000CAFE00002"); + ASSERT(ret == TEE_SUCCESS); + + /* select next should success (select D0000CAFE00001) */ + ret = tee_se_channel_select_next(c2); + ASSERT(ret == TEE_SUCCESS); + + /* should route to D0000CAFE00001 */ + ret = tee_se_channel_transmit(c2, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + ret = verify_result(resp, "D0000CAFE00001"); + ASSERT(ret == TEE_SUCCESS); + + /* + * test route to the same applet in a row from different channel + * (both should success) + */ + ret = tee_se_channel_transmit(c1, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + ret = verify_result(resp, "D0000CAFE00001"); + ASSERT(ret == TEE_SUCCESS); + + ret = tee_se_channel_transmit(c2, cmd, resp); + ASSERT(ret == TEE_SUCCESS); + ret = verify_result(resp, "D0000CAFE00001"); + ASSERT(ret == TEE_SUCCESS); + + /* clean up */ + tee_se_session_close_channel(s1, c1); + tee_se_session_close_channel(s2, c2); + + tee_se_session_close(s1); + tee_se_session_close(s2); + + tee_se_aid_release(full_aid); + tee_se_aid_release(partial_aid); + DMSG("exit"); + + return TEE_SUCCESS; +} + +static TEE_Result se_api_self_tests(uint32_t nParamTypes __unused, + TEE_Param pParams[TEE_NUM_PARAMS] __unused) +{ + size_t size = MAX_READERS; + TEE_Result ret; + struct tee_se_reader_proxy **proxies = + malloc(sizeof(void *) * MAX_READERS); + + tee_se_manager_get_readers(proxies, &size); + + ret = test_aid(proxies); + CHECK(ret); + + ret = test_select_resp(proxies); + CHECK(ret); + + ret = test_session(proxies); + CHECK(ret); + + ret = test_logical_channel(proxies); + CHECK(ret); + + ret = test_transmit(proxies); + CHECK(ret); + + ret = test_reader(proxies); + CHECK(ret); + + free(proxies); + + return TEE_SUCCESS; +} + +static TEE_Result invoke_command(void *pSessionContext __unused, + uint32_t nCommandID, uint32_t nParamTypes, + TEE_Param pParams[TEE_NUM_PARAMS]) +{ + DMSG("command entry point for static ta \"%s\"", TA_NAME); + + switch (nCommandID) { + case CMD_SELF_TESTS: + return se_api_self_tests(nParamTypes, pParams); + default: + break; + } + return TEE_ERROR_BAD_PARAMETERS; +} + +pseudo_ta_register(.uuid = SE_API_SELF_TEST_UUID, .name = TA_NAME, + .flags = PTA_DEFAULT_FLAGS, + .invoke_command_entry_point = invoke_command); |