| /* |
| * Copyright (c) 2014, STMicroelectronics International N.V. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License Version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <assert.h> |
| #include <err.h> |
| #include <malloc.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ta_crypt.h> |
| #include <ta_os_test.h> |
| #include <utee_defines.h> |
| |
| #include "xtest_helpers.h" |
| #include "xtest_test.h" |
| |
| /* Round up the even multiple of size, size has to be a multiple of 2 */ |
| #define ROUNDUP(v, size) (((v) + (size - 1)) & ~(size - 1)) |
| |
| TEEC_Context xtest_teec_ctx; |
| |
| TEEC_Result xtest_teec_ctx_init(void) |
| { |
| return TEEC_InitializeContext(_device, &xtest_teec_ctx); |
| } |
| |
| TEEC_Result xtest_teec_open_session(TEEC_Session *session, |
| const TEEC_UUID *uuid, TEEC_Operation *op, |
| uint32_t *ret_orig) |
| { |
| return TEEC_OpenSession(&xtest_teec_ctx, session, uuid, |
| TEEC_LOGIN_PUBLIC, NULL, op, ret_orig); |
| } |
| |
| void xtest_teec_ctx_deinit(void) |
| { |
| TEEC_FinalizeContext(&xtest_teec_ctx); |
| } |
| |
| TEEC_Result ta_crypt_cmd_allocate_operation(ADBG_Case_t *c, TEEC_Session *s, |
| TEE_OperationHandle *oph, |
| uint32_t algo, uint32_t mode, |
| uint32_t max_key_size) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| op.params[0].value.a = 0; |
| op.params[0].value.b = algo; |
| op.params[1].value.a = mode; |
| op.params[1].value.b = max_key_size; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_VALUE_INPUT, |
| TEEC_NONE, TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_ALLOCATE_OPERATION, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| if (res == TEEC_SUCCESS) |
| *oph = (TEE_OperationHandle)(uintptr_t)op.params[0].value.a; |
| |
| return res; |
| } |
| |
| TEEC_Result ta_crypt_cmd_allocate_transient_object(ADBG_Case_t *c, |
| TEEC_Session *s, |
| TEE_ObjectType obj_type, |
| uint32_t max_obj_size, |
| TEE_ObjectHandle *o) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| op.params[0].value.a = obj_type; |
| op.params[0].value.b = max_obj_size; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT, |
| TEEC_NONE, TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_ALLOCATE_TRANSIENT_OBJECT, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| if (res == TEEC_SUCCESS) |
| *o = (TEE_ObjectHandle)(uintptr_t)op.params[1].value.a; |
| |
| return res; |
| } |
| |
| void xtest_add_attr(size_t *attr_count, TEE_Attribute *attrs, uint32_t attr_id, |
| const void *buf, size_t len) |
| { |
| attrs[*attr_count].attributeID = attr_id; |
| attrs[*attr_count].content.ref.buffer = (void *)buf; |
| attrs[*attr_count].content.ref.length = len; |
| (*attr_count)++; |
| } |
| |
| void xtest_add_attr_value(size_t *attr_count, TEE_Attribute *attrs, |
| uint32_t attr_id, uint32_t value_a, uint32_t value_b) |
| { |
| attrs[*attr_count].attributeID = attr_id; |
| attrs[*attr_count].content.value.a = value_a; |
| attrs[*attr_count].content.value.b = value_b; |
| (*attr_count)++; |
| } |
| |
| struct tee_attr_packed { |
| uint32_t attr_id; |
| uint32_t a; |
| uint32_t b; |
| }; |
| |
| TEE_Result pack_attrs(const TEE_Attribute *attrs, uint32_t attr_count, |
| uint8_t **buf, size_t *blen) |
| { |
| struct tee_attr_packed *a = NULL; |
| uint8_t *b = NULL; |
| size_t bl = 0; |
| size_t n = 0; |
| |
| *buf = NULL; |
| *blen = 0; |
| if (attr_count == 0) |
| return TEE_SUCCESS; |
| |
| bl = sizeof(uint32_t) + sizeof(struct tee_attr_packed) * attr_count; |
| for (n = 0; n < attr_count; n++) { |
| if ((attrs[n].attributeID & TEE_ATTR_BIT_VALUE) != 0) |
| continue; /* Only memrefs need to be updated */ |
| |
| if (!attrs[n].content.ref.buffer) |
| continue; |
| |
| /* Make room for padding */ |
| bl += ROUNDUP(attrs[n].content.ref.length, 4); |
| } |
| |
| b = calloc(1, bl); |
| if (!b) |
| return TEE_ERROR_OUT_OF_MEMORY; |
| |
| *buf = b; |
| *blen = bl; |
| |
| *(uint32_t *)(void *)b = attr_count; |
| b += sizeof(uint32_t); |
| a = (struct tee_attr_packed *)(void *)b; |
| b += sizeof(struct tee_attr_packed) * attr_count; |
| |
| for (n = 0; n < attr_count; n++) { |
| a[n].attr_id = attrs[n].attributeID; |
| if (attrs[n].attributeID & TEE_ATTR_BIT_VALUE) { |
| a[n].a = attrs[n].content.value.a; |
| a[n].b = attrs[n].content.value.b; |
| continue; |
| } |
| |
| a[n].b = attrs[n].content.ref.length; |
| |
| if (!attrs[n].content.ref.buffer) { |
| a[n].a = 0; |
| continue; |
| } |
| |
| memcpy(b, attrs[n].content.ref.buffer, |
| attrs[n].content.ref.length); |
| |
| /* Make buffer pointer relative to *buf */ |
| a[n].a = (uint32_t)(uintptr_t)(b - *buf); |
| |
| /* Round up to good alignment */ |
| b += ROUNDUP(attrs[n].content.ref.length, 4); |
| } |
| |
| return TEE_SUCCESS; |
| } |
| |
| TEEC_Result ta_crypt_cmd_populate_transient_object(ADBG_Case_t *c, |
| TEEC_Session *s, |
| TEE_ObjectHandle o, |
| const TEE_Attribute *attrs, |
| uint32_t attr_count) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| uint8_t *buf = NULL; |
| size_t blen = 0; |
| |
| res = pack_attrs(attrs, attr_count, &buf, &blen); |
| if (!ADBG_EXPECT_TEEC_SUCCESS(c, res)) |
| return res; |
| |
| assert((uintptr_t)o <= UINT32_MAX); |
| op.params[0].value.a = (uint32_t)(uintptr_t)o; |
| |
| op.params[1].tmpref.buffer = buf; |
| op.params[1].tmpref.size = blen; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, |
| TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_POPULATE_TRANSIENT_OBJECT, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS && res != TEEC_ERROR_TARGET_DEAD) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| free(buf); |
| return res; |
| } |
| |
| TEE_Result ta_crypt_cmd_set_operation_key(ADBG_Case_t *c, TEEC_Session *s, |
| TEE_OperationHandle oph, |
| TEE_ObjectHandle key) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| assert((uintptr_t)oph <= UINT32_MAX); |
| op.params[0].value.a = (uint32_t)(uintptr_t)oph; |
| |
| assert((uintptr_t)key <= UINT32_MAX); |
| op.params[0].value.b = (uint32_t)(uintptr_t)key; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_SET_OPERATION_KEY, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| return res; |
| } |
| |
| TEEC_Result ta_crypt_cmd_free_transient_object(ADBG_Case_t *c, TEEC_Session *s, |
| TEE_ObjectHandle o) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| assert((uintptr_t)o <= UINT32_MAX); |
| op.params[0].value.a = (uint32_t)(uintptr_t)o; |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_FREE_TRANSIENT_OBJECT, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| return res; |
| } |
| |
| TEEC_Result ta_crypt_cmd_derive_key(ADBG_Case_t *c, TEEC_Session *s, |
| TEE_OperationHandle oph, TEE_ObjectHandle o, |
| const TEE_Attribute *params, |
| uint32_t paramCount) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| uint8_t *buf = NULL; |
| size_t blen = 0; |
| |
| res = pack_attrs(params, paramCount, &buf, &blen); |
| |
| if (!ADBG_EXPECT_TEEC_SUCCESS(c, res)) |
| return res; |
| |
| assert((uintptr_t)oph <= UINT32_MAX); |
| op.params[0].value.a = (uint32_t)(uintptr_t)oph; |
| |
| assert((uintptr_t)o <= UINT32_MAX); |
| op.params[0].value.b = (uint32_t)(uintptr_t)o; |
| |
| op.params[1].tmpref.buffer = buf; |
| op.params[1].tmpref.size = blen; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, |
| TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_DERIVE_KEY, &op, &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| free(buf); |
| return res; |
| } |
| |
| TEEC_Result ta_crypt_cmd_get_object_buffer_attribute(ADBG_Case_t *c, |
| TEEC_Session *s, |
| TEE_ObjectHandle o, |
| uint32_t attr_id, |
| void *buf, size_t *blen) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| assert((uintptr_t)o <= UINT32_MAX); |
| op.params[0].value.a = (uint32_t)(uintptr_t)o; |
| op.params[0].value.b = attr_id; |
| |
| op.params[1].tmpref.buffer = buf; |
| op.params[1].tmpref.size = *blen; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, |
| TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_GET_OBJECT_BUFFER_ATTRIBUTE, |
| &op, &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| if (res == TEEC_SUCCESS) |
| *blen = op.params[1].tmpref.size; |
| |
| return res; |
| } |
| |
| TEEC_Result ta_crypt_cmd_free_operation(ADBG_Case_t *c, TEEC_Session *s, |
| TEE_OperationHandle oph) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| |
| op.params[0].value.a = (uint32_t)(uintptr_t)oph; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, |
| TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_FREE_OPERATION, &op, |
| &ret_orig); |
| |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| } |
| |
| return res; |
| } |
| |
| bool ta_crypt_cmd_is_algo_supported(ADBG_Case_t *c, TEEC_Session *s, |
| uint32_t algo, uint32_t element) |
| { |
| TEEC_Result res = TEEC_ERROR_GENERIC; |
| TEEC_Operation op = TEEC_OPERATION_INITIALIZER; |
| uint32_t ret_orig = 0; |
| TEEC_Result st = TEEC_ERROR_GENERIC; |
| |
| op.params[0].value.a = algo; |
| op.params[0].value.b = element; |
| |
| op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT, |
| TEEC_NONE, TEEC_NONE); |
| |
| res = TEEC_InvokeCommand(s, TA_CRYPT_CMD_IS_ALGO_SUPPORTED, &op, |
| &ret_orig); |
| if (res != TEEC_SUCCESS) { |
| (void)ADBG_EXPECT_TEEC_ERROR_ORIGIN(c, TEEC_ORIGIN_TRUSTED_APP, |
| ret_orig); |
| return res; |
| } |
| |
| st = op.params[1].value.a; |
| ADBG_EXPECT_TRUE(c, st == TEEC_SUCCESS || |
| st == TEEC_ERROR_NOT_SUPPORTED); |
| if (st == TEE_SUCCESS) |
| return true; |
| return false; |
| } |
| |
| TEEC_Result ta_os_test_cmd_client_identity(TEEC_Session *session, |
| uint32_t *login, |
| TEEC_UUID *client_uuid) |
| { |
| TEEC_Operation operation = { }; |
| TEEC_Result result = TEEC_ERROR_GENERIC; |
| |
| operation.params[1].tmpref.buffer = client_uuid; |
| operation.params[1].tmpref.size = sizeof(*client_uuid); |
| |
| operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT, |
| TEEC_MEMREF_TEMP_OUTPUT, |
| TEEC_NONE, TEEC_NONE); |
| |
| result = TEEC_InvokeCommand(session, TA_OS_TEST_CMD_CLIENT_IDENTITY, |
| &operation, NULL); |
| |
| if (result != TEEC_SUCCESS) |
| return result; |
| |
| *login = operation.params[0].value.a; |
| |
| return TEEC_SUCCESS; |
| } |
| |
| void xtest_mutex_init(pthread_mutex_t *mutex) |
| { |
| int e = pthread_mutex_init(mutex, NULL); |
| |
| if (e) |
| errx(1, "pthread_mutex_init: %s", strerror(e)); |
| } |
| |
| void xtest_mutex_destroy(pthread_mutex_t *mutex) |
| { |
| int e = pthread_mutex_destroy(mutex); |
| |
| if (e) |
| errx(1, "pthread_mutex_destroy: %s", strerror(e)); |
| } |
| |
| void xtest_mutex_lock(pthread_mutex_t *mutex) |
| { |
| int e = pthread_mutex_lock(mutex); |
| |
| if (e) |
| errx(1, "pthread_mutex_lock: %s", strerror(e)); |
| } |
| |
| void xtest_mutex_unlock(pthread_mutex_t *mutex) |
| { |
| int e = pthread_mutex_unlock(mutex); |
| |
| if (e) |
| errx(1, "pthread_mutex_unlock: %s", strerror(e)); |
| } |
| |
| void xtest_barrier_init(pthread_barrier_t *barrier, unsigned count) |
| { |
| int e = pthread_barrier_init(barrier, NULL, count); |
| |
| if (e) |
| errx(1, "pthread_barrier_init: %s", strerror(e)); |
| } |
| |
| void xtest_barrier_destroy(pthread_barrier_t *barrier) |
| { |
| int e = pthread_barrier_destroy(barrier); |
| |
| if (e) |
| errx(1, "pthread_barrier_destroy: %s", strerror(e)); |
| } |
| |
| int xtest_barrier_wait(pthread_barrier_t *barrier) |
| { |
| int e = pthread_barrier_wait(barrier); |
| |
| if (e && e != PTHREAD_BARRIER_SERIAL_THREAD) |
| errx(1, "pthread _barrier_wait: %s", strerror(e)); |
| return e; |
| } |