/* * Copyright (c) 2022 Rockchip Electronics Co. Ltd. */ #include #include "rkcrypto_core_int.h" #include "rkcrypto_otp_key.h" #include "rkcrypto_trace.h" #include "tee_client_api.h" #define STORAGE_UUID { 0x2d26d8a8, 0x5134, 0x4dd8, \ { 0xb3, 0x2f, 0xb3, 0x4b, 0xce, 0xeb, 0xc4, 0x71 } } #define RK_CRYPTO_SERVICE_UUID { 0x0cacdb5d, 0x4fea, 0x466c, \ { 0x97, 0x16, 0x3d, 0x54, 0x16, 0x52, 0x83, 0x0f } } #define STORAGE_CMD_WRITE_OEM_OTP_KEY 14 #define STORAGE_CMD_SET_OEM_HR_OTP_READ_LOCK 15 #define STORAGE_CMD_OEM_OTP_KEY_IS_WRITTEN 16 #define CRYPTO_SERVICE_CMD_OEM_OTP_KEY_CIPHER 0x00000001 #define CRYPTO_SERVICE_CMD_OEM_OTP_KEY_PHYS_CIPHER 0x00000002 static const struct { const uint32_t tee_code; const uint32_t rk_crypto_code; } tee_crypto_code[] = { {TEEC_SUCCESS, RK_CRYPTO_SUCCESS}, {TEEC_ERROR_GENERIC, RK_CRYPTO_ERR_GENERIC}, {TEEC_ERROR_BAD_PARAMETERS, RK_CRYPTO_ERR_PARAMETER}, {TEEC_ERROR_BAD_STATE, RK_CRYPTO_ERR_STATE}, {TEEC_ERROR_NOT_SUPPORTED, RK_CRYPTO_ERR_NOT_SUPPORTED}, {TEEC_ERROR_OUT_OF_MEMORY, RK_CRYPTO_ERR_OUT_OF_MEMORY}, {TEEC_ERROR_ACCESS_DENIED, RK_CRYPTO_ERR_ACCESS_DENIED}, }; static RK_RES tee_to_crypto_code(uint32_t tee_code) { uint32_t i; uint32_t array_size = sizeof(tee_crypto_code) / sizeof((tee_crypto_code)[0]); for (i = 0; i < array_size; i++) { if (tee_code == tee_crypto_code[i].tee_code) return tee_crypto_code[i].rk_crypto_code; } /* Others convert to RK_CRYPTO_ERR_GENERIC. */ return RK_CRYPTO_ERR_GENERIC; } static uint32_t cipher_get_blocksize(uint32_t algo) { switch (algo) { case RK_ALGO_DES: case RK_ALGO_TDES: return DES_BLOCK_SIZE; case RK_ALGO_AES: return AES_BLOCK_SIZE; case RK_ALGO_SM4: return SM4_BLOCK_SIZE; default: return 1; } } RK_RES rk_write_oem_otp_key(enum RK_OEM_OTP_KEYID key_id, uint8_t *key, uint32_t key_len) { RK_RES res; TEEC_Context contex; TEEC_Session session; TEEC_Operation operation; TEEC_UUID uuid = STORAGE_UUID; uint32_t error_origin = 0; RK_CRYPTO_CHECK_PARAM(key_id != RK_OEM_OTP_KEY0 && key_id != RK_OEM_OTP_KEY1 && key_id != RK_OEM_OTP_KEY2 && key_id != RK_OEM_OTP_KEY3 && key_id != RK_OEM_OTP_KEY_FW); RK_CRYPTO_CHECK_PARAM(!key); RK_CRYPTO_CHECK_PARAM(key_len != 16 && key_len != 24 && key_len != 32); RK_CRYPTO_CHECK_PARAM(key_id == RK_OEM_OTP_KEY_FW && key_len != 16); res = TEEC_InitializeContext(NULL, &contex); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_InitializeContext failed with code TEEC res= 0x%x", res); return tee_to_crypto_code(res); } res = TEEC_OpenSession(&contex, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_Opensession failed with code TEEC res= 0x%x origin 0x%x", res, error_origin); goto out; } memset(&operation, 0, sizeof(TEEC_Operation)); operation.params[0].value.a = key_id; operation.params[1].tmpref.buffer = key; operation.params[1].tmpref.size = key_len; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE); res = TEEC_InvokeCommand(&session, STORAGE_CMD_WRITE_OEM_OTP_KEY, &operation, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("InvokeCommand ERR! TEEC res= 0x%x, error_origin= 0x%x", res, error_origin); } TEEC_CloseSession(&session); out: TEEC_FinalizeContext(&contex); return tee_to_crypto_code(res); } RK_RES rk_oem_otp_key_is_written(enum RK_OEM_OTP_KEYID key_id, uint8_t *is_written) { RK_RES res; TEEC_Context contex; TEEC_Session session; TEEC_Operation operation; TEEC_UUID uuid = STORAGE_UUID; uint32_t error_origin = 0; RK_CRYPTO_CHECK_PARAM(key_id != RK_OEM_OTP_KEY0 && key_id != RK_OEM_OTP_KEY1 && key_id != RK_OEM_OTP_KEY2 && key_id != RK_OEM_OTP_KEY3 && key_id != RK_OEM_OTP_KEY_FW); RK_CRYPTO_CHECK_PARAM(!is_written); res = TEEC_InitializeContext(NULL, &contex); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_InitializeContext failed with code TEEC res= 0x%x", res); return tee_to_crypto_code(res); } res = TEEC_OpenSession(&contex, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_Opensession failed with code TEEC res= 0x%x origin 0x%x", res, error_origin); goto out; } memset(&operation, 0, sizeof(TEEC_Operation)); operation.params[0].value.a = key_id; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); res = TEEC_InvokeCommand(&session, STORAGE_CMD_OEM_OTP_KEY_IS_WRITTEN, &operation, &error_origin); if (res == TEEC_ERROR_ACCESS_DENIED) { E_TRACE("Check if it has been set oem_hr_otp_read_lock!"); } else if (res != TEEC_SUCCESS) { E_TRACE("InvokeCommand ERR! TEEC res= 0x%x, error_origin= 0x%x", res, error_origin); } else { *is_written = operation.params[0].value.b; } TEEC_CloseSession(&session); out: TEEC_FinalizeContext(&contex); return tee_to_crypto_code(res); } RK_RES rk_set_oem_hr_otp_read_lock(enum RK_OEM_OTP_KEYID key_id) { RK_RES res; TEEC_Context contex; TEEC_Session session; TEEC_Operation operation; TEEC_UUID uuid = STORAGE_UUID; uint32_t error_origin = 0; RK_CRYPTO_CHECK_PARAM(key_id != RK_OEM_OTP_KEY0 && key_id != RK_OEM_OTP_KEY1 && key_id != RK_OEM_OTP_KEY2 && key_id != RK_OEM_OTP_KEY3); res = TEEC_InitializeContext(NULL, &contex); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_InitializeContext failed with code TEEC res= 0x%x", res); return tee_to_crypto_code(res); } res = TEEC_OpenSession(&contex, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_Opensession failed with code TEEC res= 0x%x origin 0x%x", res, error_origin); goto out; } memset(&operation, 0, sizeof(TEEC_Operation)); operation.params[0].value.a = key_id; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); res = TEEC_InvokeCommand(&session, STORAGE_CMD_SET_OEM_HR_OTP_READ_LOCK, &operation, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("InvokeCommand ERR! TEEC res= 0x%x, error_origin= 0x%x", res, error_origin); } TEEC_CloseSession(&session); out: TEEC_FinalizeContext(&contex); return tee_to_crypto_code(res); } RK_RES rk_oem_otp_key_cipher_virt(enum RK_OEM_OTP_KEYID key_id, rk_cipher_config *config, uint8_t *src, uint8_t *dst, uint32_t len) { RK_RES res; TEEC_Context contex; TEEC_Session session; TEEC_Operation operation; TEEC_UUID uuid = RK_CRYPTO_SERVICE_UUID; uint32_t error_origin = 0, len_aligned, len_cipher, block_size; TEEC_SharedMemory sm; RK_CRYPTO_CHECK_PARAM(key_id != RK_OEM_OTP_KEY0 && key_id != RK_OEM_OTP_KEY1 && key_id != RK_OEM_OTP_KEY2 && key_id != RK_OEM_OTP_KEY3 && key_id != RK_OEM_OTP_KEY_FW); RK_CRYPTO_CHECK_PARAM(!config || !src || !dst); RK_CRYPTO_CHECK_PARAM(config->algo != RK_ALGO_AES && config->algo != RK_ALGO_SM4); RK_CRYPTO_CHECK_PARAM(config->mode >= RK_CIPHER_MODE_XTS); RK_CRYPTO_CHECK_PARAM(config->operation != RK_OP_CIPHER_ENC && config->operation != RK_OP_CIPHER_DEC); RK_CRYPTO_CHECK_PARAM(config->key_len != 16 && config->key_len != 24 && config->key_len != 32); RK_CRYPTO_CHECK_PARAM(key_id == RK_OEM_OTP_KEY_FW && config->key_len != 16); RK_CRYPTO_CHECK_PARAM(len > RK_CRYPTO_MAX_DATA_LEN || len == 0); RK_CRYPTO_CHECK_PARAM(config->mode == RK_CIPHER_MODE_CTS && len <= 16); len_cipher = len; block_size = cipher_get_blocksize(config->algo); if (config->mode != RK_CIPHER_MODE_CTS && (len % block_size)) { RK_CRYPTO_CHECK_PARAM(config->mode != RK_CIPHER_MODE_CTR && config->mode != RK_CIPHER_MODE_CFB && config->mode != RK_CIPHER_MODE_OFB); len_aligned = ROUNDUP(len, block_size); len_cipher = len_aligned; } res = TEEC_InitializeContext(NULL, &contex); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_InitializeContext failed with code TEEC res= 0x%x", res); return tee_to_crypto_code(res); } res = TEEC_OpenSession(&contex, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_Opensession failed with code TEEC res= 0x%x origin 0x%x", res, error_origin); goto out; } sm.size = len_cipher; sm.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; res = TEEC_AllocateSharedMemory(&contex, &sm); if (res != TEEC_SUCCESS) { E_TRACE("AllocateSharedMemory ERR! TEEC res= 0x%x", res); goto out1; } memset(sm.buffer, 0, sm.size); memcpy(sm.buffer, src, len); memset(&operation, 0, sizeof(TEEC_Operation)); operation.params[0].value.a = key_id; operation.params[1].tmpref.buffer = config; operation.params[1].tmpref.size = sizeof(rk_cipher_config); operation.params[2].memref.parent = &sm; operation.params[2].memref.offset = 0; operation.params[2].memref.size = sm.size; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_PARTIAL_INOUT, TEEC_NONE); res = TEEC_InvokeCommand(&session, CRYPTO_SERVICE_CMD_OEM_OTP_KEY_CIPHER, &operation, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("InvokeCommand ERR! TEEC res= 0x%x, error_origin= 0x%x", res, error_origin); } else { memcpy(dst, sm.buffer, sm.size); } TEEC_ReleaseSharedMemory(&sm); out1: TEEC_CloseSession(&session); out: TEEC_FinalizeContext(&contex); return tee_to_crypto_code(res); } RK_RES rk_oem_otp_key_cipher(enum RK_OEM_OTP_KEYID key_id, rk_cipher_config *config, int32_t in_fd, int32_t out_fd, uint32_t len) { RK_RES res; TEEC_Context contex; TEEC_Session session; TEEC_Operation operation; TEEC_UUID uuid = RK_CRYPTO_SERVICE_UUID; uint32_t error_origin = 0, len_aligned, len_cipher, block_size; struct crypt_fd_map_op src_mop; struct crypt_fd_map_op dst_mop; RK_CRYPTO_CHECK_PARAM(in_fd < 0); RK_CRYPTO_CHECK_PARAM(out_fd < 0); RK_CRYPTO_CHECK_PARAM(key_id != RK_OEM_OTP_KEY0 && key_id != RK_OEM_OTP_KEY1 && key_id != RK_OEM_OTP_KEY2 && key_id != RK_OEM_OTP_KEY3 && key_id != RK_OEM_OTP_KEY_FW); RK_CRYPTO_CHECK_PARAM(!config); RK_CRYPTO_CHECK_PARAM(config->algo != RK_ALGO_AES && config->algo != RK_ALGO_SM4); RK_CRYPTO_CHECK_PARAM(config->mode >= RK_CIPHER_MODE_XTS); RK_CRYPTO_CHECK_PARAM(config->operation != RK_OP_CIPHER_ENC && config->operation != RK_OP_CIPHER_DEC); RK_CRYPTO_CHECK_PARAM(config->key_len != 16 && config->key_len != 24 && config->key_len != 32); RK_CRYPTO_CHECK_PARAM(key_id == RK_OEM_OTP_KEY_FW && config->key_len != 16); RK_CRYPTO_CHECK_PARAM(len == 0); RK_CRYPTO_CHECK_PARAM(config->mode == RK_CIPHER_MODE_CTS && len <= 16); len_cipher = len; block_size = cipher_get_blocksize(config->algo); if (config->mode != RK_CIPHER_MODE_CTS && (len % block_size)) { RK_CRYPTO_CHECK_PARAM(config->mode != RK_CIPHER_MODE_CTR && config->mode != RK_CIPHER_MODE_CFB && config->mode != RK_CIPHER_MODE_OFB); len_aligned = ROUNDUP(len, block_size); len_cipher = len_aligned; } memset(&src_mop, 0, sizeof(src_mop)); memset(&dst_mop, 0, sizeof(dst_mop)); src_mop.dma_fd = in_fd; dst_mop.dma_fd = out_fd; res = rk_crypto_fd_ioctl(RIOCCRYPT_FD_MAP, &src_mop); if (res != RK_CRYPTO_SUCCESS) { E_TRACE("RIOCCRYPT_FD_MAP failed, res= 0x%x", res); return res; } if (out_fd != in_fd) { res = rk_crypto_fd_ioctl(RIOCCRYPT_FD_MAP, &dst_mop); if (res != RK_CRYPTO_SUCCESS) { E_TRACE("RIOCCRYPT_FD_MAP failed, res= 0x%x", res); goto out; } } else { dst_mop.phys_addr = src_mop.phys_addr; } res = TEEC_InitializeContext(NULL, &contex); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_InitializeContext failed with code TEEC res= 0x%x", res); goto out1; } res = TEEC_OpenSession(&contex, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("TEEC_Opensession failed with code TEEC res= 0x%x origin 0x%x", res, error_origin); goto out2; } memset(&operation, 0, sizeof(TEEC_Operation)); operation.params[0].value.a = key_id; operation.params[1].tmpref.buffer = config; operation.params[1].tmpref.size = sizeof(rk_cipher_config); operation.params[2].value.a = src_mop.phys_addr; operation.params[2].value.b = len_cipher; operation.params[3].value.a = dst_mop.phys_addr; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_INPUT, TEEC_VALUE_INPUT); res = TEEC_InvokeCommand(&session, CRYPTO_SERVICE_CMD_OEM_OTP_KEY_PHYS_CIPHER, &operation, &error_origin); if (res != TEEC_SUCCESS) { E_TRACE("InvokeCommand ERR! TEEC res= 0x%x, error_origin= 0x%x", res, error_origin); } TEEC_CloseSession(&session); out2: TEEC_FinalizeContext(&contex); out1: res = tee_to_crypto_code(res); if (out_fd != in_fd) rk_crypto_fd_ioctl(RIOCCRYPT_FD_UNMAP, &dst_mop); out: rk_crypto_fd_ioctl(RIOCCRYPT_FD_UNMAP, &src_mop); return res; }