| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2014, STMicroelectronics International N.V. |
| */ |
| #include <stdlib.h> |
| #include <string.h> |
| #include <string_ext.h> |
| |
| #include <tee_api.h> |
| #include <tee_api_defines_extensions.h> |
| #include <tee_internal_api_extensions.h> |
| #include <utee_syscalls.h> |
| #include <utee_defines.h> |
| #include <util.h> |
| #include "tee_api_private.h" |
| |
| struct __TEE_OperationHandle { |
| TEE_OperationInfo info; |
| TEE_ObjectHandle key1; |
| TEE_ObjectHandle key2; |
| uint32_t operationState;/* Operation state : INITIAL or ACTIVE */ |
| uint8_t *buffer; /* buffer to collect complete blocks */ |
| bool buffer_two_blocks; /* True if two blocks need to be buffered */ |
| size_t block_size; /* Block size of cipher */ |
| size_t buffer_offs; /* Offset in buffer */ |
| uint32_t state; /* Handle to state in TEE Core */ |
| uint32_t ae_tag_len; /* |
| * tag_len in bytes for AE operation else unused |
| */ |
| }; |
| |
| /* Cryptographic Operations API - Generic Operation Functions */ |
| |
| TEE_Result TEE_AllocateOperation(TEE_OperationHandle *operation, |
| uint32_t algorithm, uint32_t mode, |
| uint32_t maxKeySize) |
| { |
| TEE_Result res; |
| TEE_OperationHandle op = TEE_HANDLE_NULL; |
| uint32_t handle_state = 0; |
| size_t block_size = 1; |
| uint32_t req_key_usage; |
| bool with_private_key = false; |
| bool buffer_two_blocks = false; |
| |
| if (!operation) |
| TEE_Panic(0); |
| |
| if (algorithm == TEE_ALG_AES_XTS || algorithm == TEE_ALG_SM2_KEP) |
| handle_state = TEE_HANDLE_FLAG_EXPECT_TWO_KEYS; |
| |
| /* Check algorithm max key size */ |
| switch (algorithm) { |
| case TEE_ALG_DSA_SHA1: |
| if (maxKeySize < 512) |
| return TEE_ERROR_NOT_SUPPORTED; |
| if (maxKeySize > 1024) |
| return TEE_ERROR_NOT_SUPPORTED; |
| if (maxKeySize % 64 != 0) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_DSA_SHA224: |
| if (maxKeySize != 2048) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_DSA_SHA256: |
| if (maxKeySize != 2048 && maxKeySize != 3072) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_ECDSA_P192: |
| case TEE_ALG_ECDH_P192: |
| if (maxKeySize != 192) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_ECDSA_P224: |
| case TEE_ALG_ECDH_P224: |
| if (maxKeySize != 224) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_ECDSA_P256: |
| case TEE_ALG_ECDH_P256: |
| case TEE_ALG_SM2_PKE: |
| case TEE_ALG_SM2_DSA_SM3: |
| if (maxKeySize != 256) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_SM2_KEP: |
| /* Two 256-bit keys */ |
| if (maxKeySize != 512) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_ECDSA_P384: |
| case TEE_ALG_ECDH_P384: |
| if (maxKeySize != 384) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| case TEE_ALG_ECDSA_P521: |
| case TEE_ALG_ECDH_P521: |
| if (maxKeySize != 521) |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Check algorithm mode */ |
| switch (algorithm) { |
| case TEE_ALG_AES_CTS: |
| case TEE_ALG_AES_XTS: |
| buffer_two_blocks = true; |
| /* FALLTHROUGH */ |
| case TEE_ALG_AES_ECB_NOPAD: |
| case TEE_ALG_AES_CBC_NOPAD: |
| case TEE_ALG_AES_CCM: |
| case TEE_ALG_DES_ECB_NOPAD: |
| case TEE_ALG_DES_CBC_NOPAD: |
| case TEE_ALG_DES3_ECB_NOPAD: |
| case TEE_ALG_DES3_CBC_NOPAD: |
| case TEE_ALG_SM4_ECB_NOPAD: |
| case TEE_ALG_SM4_CBC_NOPAD: |
| case TEE_ALG_SM4_CTR: |
| if (TEE_ALG_GET_MAIN_ALG(algorithm) == TEE_MAIN_ALGO_AES) |
| block_size = TEE_AES_BLOCK_SIZE; |
| else if (TEE_ALG_GET_MAIN_ALG(algorithm) == TEE_MAIN_ALGO_SM4) |
| block_size = TEE_SM4_BLOCK_SIZE; |
| else |
| block_size = TEE_DES_BLOCK_SIZE; |
| /* FALLTHROUGH */ |
| case TEE_ALG_AES_CTR: |
| case TEE_ALG_AES_GCM: |
| if (mode == TEE_MODE_ENCRYPT) |
| req_key_usage = TEE_USAGE_ENCRYPT; |
| else if (mode == TEE_MODE_DECRYPT) |
| req_key_usage = TEE_USAGE_DECRYPT; |
| else |
| return TEE_ERROR_NOT_SUPPORTED; |
| break; |
| |
| #if defined(CFG_CRYPTO_RSASSA_NA1) |
| case TEE_ALG_RSASSA_PKCS1_V1_5: |
| #endif |
| case TEE_ALG_RSASSA_PKCS1_V1_5_MD5: |
| case TEE_ALG_RSASSA_PKCS1_V1_5_SHA1: |
| case TEE_ALG_RSASSA_PKCS1_V1_5_SHA224: |
| case TEE_ALG_RSASSA_PKCS1_V1_5_SHA256: |
| case TEE_ALG_RSASSA_PKCS1_V1_5_SHA384: |
| case TEE_ALG_RSASSA_PKCS1_V1_5_SHA512: |
| case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA1: |
| case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA224: |
| case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256: |
| case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA384: |
| case TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA512: |
| case TEE_ALG_DSA_SHA1: |
| case TEE_ALG_DSA_SHA224: |
| case TEE_ALG_DSA_SHA256: |
| case TEE_ALG_ECDSA_P192: |
| case TEE_ALG_ECDSA_P224: |
| case TEE_ALG_ECDSA_P256: |
| case TEE_ALG_ECDSA_P384: |
| case TEE_ALG_ECDSA_P521: |
| case TEE_ALG_SM2_DSA_SM3: |
| if (mode == TEE_MODE_SIGN) { |
| with_private_key = true; |
| req_key_usage = TEE_USAGE_SIGN; |
| } else if (mode == TEE_MODE_VERIFY) { |
| req_key_usage = TEE_USAGE_VERIFY; |
| } else { |
| return TEE_ERROR_NOT_SUPPORTED; |
| } |
| break; |
| |
| case TEE_ALG_RSAES_PKCS1_V1_5: |
| case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA1: |
| case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA224: |
| case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA256: |
| case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA384: |
| case TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA512: |
| case TEE_ALG_SM2_PKE: |
| if (mode == TEE_MODE_ENCRYPT) { |
| req_key_usage = TEE_USAGE_ENCRYPT; |
| } else if (mode == TEE_MODE_DECRYPT) { |
| with_private_key = true; |
| req_key_usage = TEE_USAGE_DECRYPT; |
| } else { |
| return TEE_ERROR_NOT_SUPPORTED; |
| } |
| break; |
| |
| case TEE_ALG_RSA_NOPAD: |
| if (mode == TEE_MODE_ENCRYPT) { |
| req_key_usage = TEE_USAGE_ENCRYPT | TEE_USAGE_VERIFY; |
| } else if (mode == TEE_MODE_DECRYPT) { |
| with_private_key = true; |
| req_key_usage = TEE_USAGE_DECRYPT | TEE_USAGE_SIGN; |
| } else { |
| return TEE_ERROR_NOT_SUPPORTED; |
| } |
| break; |
| |
| case TEE_ALG_DH_DERIVE_SHARED_SECRET: |
| case TEE_ALG_ECDH_P192: |
| case TEE_ALG_ECDH_P224: |
| case TEE_ALG_ECDH_P256: |
| case TEE_ALG_ECDH_P384: |
| case TEE_ALG_ECDH_P521: |
| case TEE_ALG_HKDF_MD5_DERIVE_KEY: |
| case TEE_ALG_HKDF_SHA1_DERIVE_KEY: |
| case TEE_ALG_HKDF_SHA224_DERIVE_KEY: |
| case TEE_ALG_HKDF_SHA256_DERIVE_KEY: |
| case TEE_ALG_HKDF_SHA384_DERIVE_KEY: |
| case TEE_ALG_HKDF_SHA512_DERIVE_KEY: |
| case TEE_ALG_CONCAT_KDF_SHA1_DERIVE_KEY: |
| case TEE_ALG_CONCAT_KDF_SHA224_DERIVE_KEY: |
| case TEE_ALG_CONCAT_KDF_SHA256_DERIVE_KEY: |
| case TEE_ALG_CONCAT_KDF_SHA384_DERIVE_KEY: |
| case TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY: |
| case TEE_ALG_PBKDF2_HMAC_SHA1_DERIVE_KEY: |
| case TEE_ALG_SM2_KEP: |
| if (mode != TEE_MODE_DERIVE) |
| return TEE_ERROR_NOT_SUPPORTED; |
| with_private_key = true; |
| req_key_usage = TEE_USAGE_DERIVE; |
| break; |
| |
| case TEE_ALG_MD5: |
| case TEE_ALG_SHA1: |
| case TEE_ALG_SHA224: |
| case TEE_ALG_SHA256: |
| case TEE_ALG_SHA384: |
| case TEE_ALG_SHA512: |
| case TEE_ALG_SM3: |
| if (mode != TEE_MODE_DIGEST) |
| return TEE_ERROR_NOT_SUPPORTED; |
| /* v1.1: flags always set for digest operations */ |
| handle_state |= TEE_HANDLE_FLAG_KEY_SET; |
| req_key_usage = 0; |
| break; |
| |
| case TEE_ALG_DES_CBC_MAC_NOPAD: |
| case TEE_ALG_AES_CBC_MAC_NOPAD: |
| case TEE_ALG_AES_CBC_MAC_PKCS5: |
| case TEE_ALG_AES_CMAC: |
| case TEE_ALG_DES_CBC_MAC_PKCS5: |
| case TEE_ALG_DES3_CBC_MAC_NOPAD: |
| case TEE_ALG_DES3_CBC_MAC_PKCS5: |
| case TEE_ALG_HMAC_MD5: |
| case TEE_ALG_HMAC_SHA1: |
| case TEE_ALG_HMAC_SHA224: |
| case TEE_ALG_HMAC_SHA256: |
| case TEE_ALG_HMAC_SHA384: |
| case TEE_ALG_HMAC_SHA512: |
| case TEE_ALG_HMAC_SM3: |
| if (mode != TEE_MODE_MAC) |
| return TEE_ERROR_NOT_SUPPORTED; |
| req_key_usage = TEE_USAGE_MAC; |
| break; |
| |
| default: |
| return TEE_ERROR_NOT_SUPPORTED; |
| } |
| |
| op = TEE_Malloc(sizeof(*op), TEE_MALLOC_FILL_ZERO); |
| if (!op) |
| return TEE_ERROR_OUT_OF_MEMORY; |
| |
| op->info.algorithm = algorithm; |
| op->info.operationClass = TEE_ALG_GET_CLASS(algorithm); |
| #ifdef CFG_CRYPTO_RSASSA_NA1 |
| if (algorithm == TEE_ALG_RSASSA_PKCS1_V1_5) |
| op->info.operationClass = TEE_OPERATION_ASYMMETRIC_SIGNATURE; |
| #endif |
| op->info.mode = mode; |
| op->info.maxKeySize = maxKeySize; |
| op->info.requiredKeyUsage = req_key_usage; |
| op->info.handleState = handle_state; |
| |
| if (block_size > 1) { |
| size_t buffer_size = block_size; |
| |
| if (buffer_two_blocks) |
| buffer_size *= 2; |
| |
| op->buffer = TEE_Malloc(buffer_size, |
| TEE_USER_MEM_HINT_NO_FILL_ZERO); |
| if (op->buffer == NULL) { |
| res = TEE_ERROR_OUT_OF_MEMORY; |
| goto out; |
| } |
| } |
| op->block_size = block_size; |
| op->buffer_two_blocks = buffer_two_blocks; |
| |
| if (TEE_ALG_GET_CLASS(algorithm) != TEE_OPERATION_DIGEST) { |
| uint32_t mks = maxKeySize; |
| TEE_ObjectType key_type = TEE_ALG_GET_KEY_TYPE(algorithm, |
| with_private_key); |
| |
| /* |
| * If two keys are expected the max key size is the sum of |
| * the size of both keys. |
| */ |
| if (op->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) |
| mks /= 2; |
| |
| res = TEE_AllocateTransientObject(key_type, mks, &op->key1); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| if (op->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) { |
| res = TEE_AllocateTransientObject(key_type, mks, |
| &op->key2); |
| if (res != TEE_SUCCESS) |
| goto out; |
| } |
| } |
| |
| res = utee_cryp_state_alloc(algorithm, mode, (unsigned long)op->key1, |
| (unsigned long)op->key2, &op->state); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| /* |
| * Initialize digest operations |
| * Other multi-stage operations initialized w/ TEE_xxxInit functions |
| * Non-applicable on asymmetric operations |
| */ |
| if (TEE_ALG_GET_CLASS(algorithm) == TEE_OPERATION_DIGEST) { |
| res = utee_hash_init(op->state, NULL, 0); |
| if (res != TEE_SUCCESS) |
| goto out; |
| /* v1.1: flags always set for digest operations */ |
| op->info.handleState |= TEE_HANDLE_FLAG_INITIALIZED; |
| } |
| |
| op->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| *operation = op; |
| |
| out: |
| if (res != TEE_SUCCESS) { |
| if (res != TEE_ERROR_OUT_OF_MEMORY && |
| res != TEE_ERROR_NOT_SUPPORTED) |
| TEE_Panic(res); |
| if (op) { |
| if (op->state) { |
| TEE_FreeOperation(op); |
| } else { |
| TEE_Free(op->buffer); |
| TEE_FreeTransientObject(op->key1); |
| TEE_FreeTransientObject(op->key2); |
| TEE_Free(op); |
| } |
| } |
| } |
| |
| return res; |
| } |
| |
| void TEE_FreeOperation(TEE_OperationHandle operation) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| |
| /* |
| * Note that keys should not be freed here, since they are |
| * claimed by the operation they will be freed by |
| * utee_cryp_state_free(). |
| */ |
| res = utee_cryp_state_free(operation->state); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| |
| TEE_Free(operation->buffer); |
| TEE_Free(operation); |
| } |
| |
| void TEE_GetOperationInfo(TEE_OperationHandle operation, |
| TEE_OperationInfo *operationInfo) |
| { |
| if (operation == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| |
| if (!operationInfo) |
| TEE_Panic(0); |
| |
| *operationInfo = operation->info; |
| } |
| |
| TEE_Result TEE_GetOperationInfoMultiple(TEE_OperationHandle operation, |
| TEE_OperationInfoMultiple *operationInfoMultiple, |
| uint32_t *operationSize) |
| { |
| TEE_Result res = TEE_SUCCESS; |
| TEE_ObjectInfo key_info1; |
| TEE_ObjectInfo key_info2; |
| uint32_t num_of_keys; |
| size_t n; |
| |
| if (operation == TEE_HANDLE_NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (!operationInfoMultiple) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (!operationSize) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| num_of_keys = (*operationSize-sizeof(TEE_OperationInfoMultiple))/ |
| sizeof(TEE_OperationInfoKey); |
| |
| if (num_of_keys > 2) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* Two keys flag (TEE_ALG_AES_XTS only) */ |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) != |
| 0 && |
| (num_of_keys != 2)) { |
| res = TEE_ERROR_SHORT_BUFFER; |
| goto out; |
| } |
| |
| /* Clear */ |
| for (n = 0; n < num_of_keys; n++) { |
| operationInfoMultiple->keyInformation[n].keySize = 0; |
| operationInfoMultiple->keyInformation[n].requiredKeyUsage = 0; |
| } |
| |
| if (num_of_keys == 2) { |
| res = TEE_GetObjectInfo1(operation->key2, &key_info2); |
| /* Key2 is not a valid handle */ |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| operationInfoMultiple->keyInformation[1].keySize = |
| key_info2.keySize; |
| operationInfoMultiple->keyInformation[1].requiredKeyUsage = |
| operation->info.requiredKeyUsage; |
| } |
| |
| if (num_of_keys >= 1) { |
| res = TEE_GetObjectInfo1(operation->key1, &key_info1); |
| /* Key1 is not a valid handle */ |
| if (res != TEE_SUCCESS) { |
| if (num_of_keys == 2) { |
| operationInfoMultiple->keyInformation[1]. |
| keySize = 0; |
| operationInfoMultiple->keyInformation[1]. |
| requiredKeyUsage = 0; |
| } |
| goto out; |
| } |
| |
| operationInfoMultiple->keyInformation[0].keySize = |
| key_info1.keySize; |
| operationInfoMultiple->keyInformation[0].requiredKeyUsage = |
| operation->info.requiredKeyUsage; |
| } |
| |
| /* No key */ |
| operationInfoMultiple->algorithm = operation->info.algorithm; |
| operationInfoMultiple->operationClass = operation->info.operationClass; |
| operationInfoMultiple->mode = operation->info.mode; |
| operationInfoMultiple->digestLength = operation->info.digestLength; |
| operationInfoMultiple->maxKeySize = operation->info.maxKeySize; |
| operationInfoMultiple->handleState = operation->info.handleState; |
| operationInfoMultiple->operationState = operation->operationState; |
| operationInfoMultiple->numberOfKeys = num_of_keys; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| void TEE_ResetOperation(TEE_OperationHandle operation) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| |
| if (!(operation->info.handleState & TEE_HANDLE_FLAG_KEY_SET)) |
| TEE_Panic(0); |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| if (operation->info.operationClass == TEE_OPERATION_DIGEST) { |
| res = utee_hash_init(operation->state, NULL, 0); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| operation->info.handleState |= TEE_HANDLE_FLAG_INITIALIZED; |
| } else { |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_INITIALIZED; |
| } |
| } |
| |
| TEE_Result TEE_SetOperationKey(TEE_OperationHandle operation, |
| TEE_ObjectHandle key) |
| { |
| TEE_Result res; |
| uint32_t key_size = 0; |
| TEE_ObjectInfo key_info; |
| |
| if (operation == TEE_HANDLE_NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_INITIAL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (key == TEE_HANDLE_NULL) { |
| /* Operation key cleared */ |
| TEE_ResetTransientObject(operation->key1); |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* No key for digest operation */ |
| if (operation->info.operationClass == TEE_OPERATION_DIGEST) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* Two keys flag not expected (TEE_ALG_AES_XTS excluded) */ |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) != |
| 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| res = TEE_GetObjectInfo1(key, &key_info); |
| /* Key is not a valid handle */ |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| /* Supplied key has to meet required usage */ |
| if ((key_info.objectUsage & operation->info.requiredKeyUsage) != |
| operation->info.requiredKeyUsage) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.maxKeySize < key_info.keySize) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| key_size = key_info.keySize; |
| |
| TEE_ResetTransientObject(operation->key1); |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_KEY_SET; |
| |
| res = TEE_CopyObjectAttributes1(operation->key1, key); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| operation->info.handleState |= TEE_HANDLE_FLAG_KEY_SET; |
| |
| operation->info.keySize = key_size; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_CORRUPT_OBJECT && |
| res != TEE_ERROR_STORAGE_NOT_AVAILABLE) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_SetOperationKey2(TEE_OperationHandle operation, |
| TEE_ObjectHandle key1, TEE_ObjectHandle key2) |
| { |
| TEE_Result res; |
| uint32_t key_size = 0; |
| TEE_ObjectInfo key_info1; |
| TEE_ObjectInfo key_info2; |
| |
| if (operation == TEE_HANDLE_NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_INITIAL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * Key1/Key2 and/or are not initialized and |
| * Either both keys are NULL or both are not NULL |
| */ |
| if (key1 == TEE_HANDLE_NULL || key2 == TEE_HANDLE_NULL) { |
| /* Clear operation key1 (if needed) */ |
| if (key1 == TEE_HANDLE_NULL) |
| TEE_ResetTransientObject(operation->key1); |
| /* Clear operation key2 (if needed) */ |
| if (key2 == TEE_HANDLE_NULL) |
| TEE_ResetTransientObject(operation->key2); |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* No key for digest operation */ |
| if (operation->info.operationClass == TEE_OPERATION_DIGEST) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* Two keys flag expected (TEE_ALG_AES_XTS and TEE_ALG_SM2_KEP only) */ |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) == |
| 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| res = TEE_GetObjectInfo1(key1, &key_info1); |
| /* Key1 is not a valid handle */ |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| /* Supplied key has to meet required usage */ |
| if ((key_info1.objectUsage & operation->info. |
| requiredKeyUsage) != operation->info.requiredKeyUsage) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| res = TEE_GetObjectInfo1(key2, &key_info2); |
| /* Key2 is not a valid handle */ |
| if (res != TEE_SUCCESS) { |
| if (res == TEE_ERROR_CORRUPT_OBJECT) |
| res = TEE_ERROR_CORRUPT_OBJECT_2; |
| goto out; |
| } |
| |
| /* Supplied key has to meet required usage */ |
| if ((key_info2.objectUsage & operation->info. |
| requiredKeyUsage) != operation->info.requiredKeyUsage) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * All the multi key algorithm currently supported requires the keys to |
| * be of equal size. |
| */ |
| if (key_info1.keySize != key_info2.keySize) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| |
| } |
| |
| if (operation->info.maxKeySize < key_info1.keySize) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * Odd that only the size of one key should be reported while |
| * size of two key are used when allocating the operation. |
| */ |
| key_size = key_info1.keySize; |
| |
| TEE_ResetTransientObject(operation->key1); |
| TEE_ResetTransientObject(operation->key2); |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_KEY_SET; |
| |
| res = TEE_CopyObjectAttributes1(operation->key1, key1); |
| if (res != TEE_SUCCESS) |
| goto out; |
| res = TEE_CopyObjectAttributes1(operation->key2, key2); |
| if (res != TEE_SUCCESS) { |
| if (res == TEE_ERROR_CORRUPT_OBJECT) |
| res = TEE_ERROR_CORRUPT_OBJECT_2; |
| goto out; |
| } |
| |
| operation->info.handleState |= TEE_HANDLE_FLAG_KEY_SET; |
| |
| operation->info.keySize = key_size; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_CORRUPT_OBJECT && |
| res != TEE_ERROR_CORRUPT_OBJECT_2 && |
| res != TEE_ERROR_STORAGE_NOT_AVAILABLE && |
| res != TEE_ERROR_STORAGE_NOT_AVAILABLE_2) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| void TEE_CopyOperation(TEE_OperationHandle dst_op, TEE_OperationHandle src_op) |
| { |
| TEE_Result res; |
| |
| if (dst_op == TEE_HANDLE_NULL || src_op == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| if (dst_op->info.algorithm != src_op->info.algorithm) |
| TEE_Panic(0); |
| if (src_op->info.operationClass != TEE_OPERATION_DIGEST) { |
| TEE_ObjectHandle key1 = TEE_HANDLE_NULL; |
| TEE_ObjectHandle key2 = TEE_HANDLE_NULL; |
| |
| if (src_op->info.handleState & TEE_HANDLE_FLAG_KEY_SET) { |
| key1 = src_op->key1; |
| key2 = src_op->key2; |
| } |
| |
| if ((src_op->info.handleState & |
| TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) == 0) { |
| TEE_SetOperationKey(dst_op, key1); |
| } else { |
| TEE_SetOperationKey2(dst_op, key1, key2); |
| } |
| } |
| dst_op->info.handleState = src_op->info.handleState; |
| dst_op->info.keySize = src_op->info.keySize; |
| dst_op->operationState = src_op->operationState; |
| |
| if (dst_op->buffer_two_blocks != src_op->buffer_two_blocks || |
| dst_op->block_size != src_op->block_size) |
| TEE_Panic(0); |
| |
| if (dst_op->buffer != NULL) { |
| if (src_op->buffer == NULL) |
| TEE_Panic(0); |
| |
| memcpy(dst_op->buffer, src_op->buffer, src_op->buffer_offs); |
| dst_op->buffer_offs = src_op->buffer_offs; |
| } else if (src_op->buffer != NULL) { |
| TEE_Panic(0); |
| } |
| |
| res = utee_cryp_state_copy(dst_op->state, src_op->state); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| /* Cryptographic Operations API - Message Digest Functions */ |
| |
| static void init_hash_operation(TEE_OperationHandle operation, const void *IV, |
| uint32_t IVLen) |
| { |
| TEE_Result res; |
| |
| /* |
| * Note : IV and IVLen are never used in current implementation |
| * This is why coherent values of IV and IVLen are not checked |
| */ |
| res = utee_hash_init(operation->state, IV, IVLen); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| operation->buffer_offs = 0; |
| operation->info.handleState |= TEE_HANDLE_FLAG_INITIALIZED; |
| } |
| |
| void TEE_DigestUpdate(TEE_OperationHandle operation, |
| const void *chunk, uint32_t chunkSize) |
| { |
| TEE_Result res = TEE_ERROR_GENERIC; |
| |
| if (operation == TEE_HANDLE_NULL || |
| operation->info.operationClass != TEE_OPERATION_DIGEST) |
| TEE_Panic(0); |
| |
| operation->operationState = TEE_OPERATION_STATE_ACTIVE; |
| |
| res = utee_hash_update(operation->state, chunk, chunkSize); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| TEE_Result TEE_DigestDoFinal(TEE_OperationHandle operation, const void *chunk, |
| uint32_t chunkLen, void *hash, uint32_t *hashLen) |
| { |
| TEE_Result res; |
| uint64_t hl; |
| |
| if ((operation == TEE_HANDLE_NULL) || |
| (!chunk && chunkLen) || |
| !hash || |
| !hashLen || |
| (operation->info.operationClass != TEE_OPERATION_DIGEST)) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| hl = *hashLen; |
| res = utee_hash_final(operation->state, chunk, chunkLen, hash, &hl); |
| *hashLen = hl; |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| /* Reset operation state */ |
| init_hash_operation(operation, NULL, 0); |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| /* Cryptographic Operations API - Symmetric Cipher Functions */ |
| |
| void TEE_CipherInit(TEE_OperationHandle operation, const void *IV, |
| uint32_t IVLen) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| |
| if (operation->info.operationClass != TEE_OPERATION_CIPHER) |
| TEE_Panic(0); |
| |
| if (!(operation->info.handleState & TEE_HANDLE_FLAG_KEY_SET) || |
| !(operation->key1)) |
| TEE_Panic(0); |
| |
| if (operation->operationState != TEE_OPERATION_STATE_INITIAL) |
| TEE_ResetOperation(operation); |
| |
| operation->operationState = TEE_OPERATION_STATE_ACTIVE; |
| |
| res = utee_cipher_init(operation->state, IV, IVLen); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| |
| operation->buffer_offs = 0; |
| operation->info.handleState |= TEE_HANDLE_FLAG_INITIALIZED; |
| } |
| |
| static TEE_Result tee_buffer_update( |
| TEE_OperationHandle op, |
| TEE_Result(*update_func)(unsigned long state, const void *src, |
| size_t slen, void *dst, uint64_t *dlen), |
| const void *src_data, size_t src_len, |
| void *dest_data, uint64_t *dest_len) |
| { |
| TEE_Result res; |
| const uint8_t *src = src_data; |
| size_t slen = src_len; |
| uint8_t *dst = dest_data; |
| size_t dlen = *dest_len; |
| size_t acc_dlen = 0; |
| uint64_t tmp_dlen; |
| size_t l; |
| size_t buffer_size; |
| size_t buffer_left; |
| |
| if (!src) { |
| if (slen) |
| TEE_Panic(0); |
| goto out; |
| } |
| |
| if (op->buffer_two_blocks) { |
| buffer_size = op->block_size * 2; |
| buffer_left = 1; |
| } else { |
| buffer_size = op->block_size; |
| buffer_left = 0; |
| } |
| |
| if (op->buffer_offs > 0) { |
| /* Fill up complete block */ |
| if (op->buffer_offs < op->block_size) |
| l = MIN(slen, op->block_size - op->buffer_offs); |
| else |
| l = MIN(slen, buffer_size - op->buffer_offs); |
| memcpy(op->buffer + op->buffer_offs, src, l); |
| op->buffer_offs += l; |
| src += l; |
| slen -= l; |
| if ((op->buffer_offs % op->block_size) != 0) |
| goto out; /* Nothing left to do */ |
| } |
| |
| /* If we can feed from buffer */ |
| if ((op->buffer_offs > 0) && |
| ((op->buffer_offs + slen) >= (buffer_size + buffer_left))) { |
| l = ROUNDUP(op->buffer_offs + slen - buffer_size, |
| op->block_size); |
| l = MIN(op->buffer_offs, l); |
| tmp_dlen = dlen; |
| res = update_func(op->state, op->buffer, l, dst, &tmp_dlen); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| dst += tmp_dlen; |
| dlen -= tmp_dlen; |
| acc_dlen += tmp_dlen; |
| op->buffer_offs -= l; |
| if (op->buffer_offs > 0) { |
| /* |
| * Slen is small enough to be contained in rest buffer. |
| */ |
| memcpy(op->buffer, op->buffer + l, buffer_size - l); |
| memcpy(op->buffer + op->buffer_offs, src, slen); |
| op->buffer_offs += slen; |
| goto out; /* Nothing left to do */ |
| } |
| } |
| |
| if (slen >= (buffer_size + buffer_left)) { |
| /* Buffer is empty, feed as much as possible from src */ |
| if (op->info.algorithm == TEE_ALG_AES_CTS) |
| l = ROUNDUP(slen - buffer_size, op->block_size); |
| else |
| l = ROUNDUP(slen - buffer_size + 1, op->block_size); |
| |
| tmp_dlen = dlen; |
| res = update_func(op->state, src, l, dst, &tmp_dlen); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| src += l; |
| slen -= l; |
| dst += tmp_dlen; |
| dlen -= tmp_dlen; |
| acc_dlen += tmp_dlen; |
| } |
| |
| /* Slen is small enough to be contained in buffer. */ |
| memcpy(op->buffer + op->buffer_offs, src, slen); |
| op->buffer_offs += slen; |
| |
| out: |
| *dest_len = acc_dlen; |
| return TEE_SUCCESS; |
| } |
| |
| TEE_Result TEE_CipherUpdate(TEE_OperationHandle operation, const void *srcData, |
| uint32_t srcLen, void *destData, uint32_t *destLen) |
| { |
| TEE_Result res; |
| size_t req_dlen; |
| uint64_t dl; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (srcData == NULL && srcLen != 0) || |
| destLen == NULL || |
| (destData == NULL && *destLen != 0)) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_CIPHER) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (!srcData && !srcLen) { |
| *destLen = 0; |
| res = TEE_SUCCESS; |
| goto out; |
| } |
| |
| /* Calculate required dlen */ |
| if (operation->block_size > 1) { |
| req_dlen = ((operation->buffer_offs + srcLen) / |
| operation->block_size) * operation->block_size; |
| } else { |
| req_dlen = srcLen; |
| } |
| if (operation->buffer_two_blocks) { |
| if (req_dlen > operation->block_size * 2) |
| req_dlen -= operation->block_size * 2; |
| else |
| req_dlen = 0; |
| } |
| /* |
| * Check that required destLen is big enough before starting to feed |
| * data to the algorithm. Errors during feeding of data are fatal as we |
| * can't restore sync with this API. |
| */ |
| if (*destLen < req_dlen) { |
| *destLen = req_dlen; |
| res = TEE_ERROR_SHORT_BUFFER; |
| goto out; |
| } |
| |
| dl = *destLen; |
| if (operation->block_size > 1) { |
| res = tee_buffer_update(operation, utee_cipher_update, srcData, |
| srcLen, destData, &dl); |
| } else { |
| if (srcLen > 0) { |
| res = utee_cipher_update(operation->state, srcData, |
| srcLen, destData, &dl); |
| } else { |
| res = TEE_SUCCESS; |
| dl = 0; |
| } |
| } |
| *destLen = dl; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_CipherDoFinal(TEE_OperationHandle operation, |
| const void *srcData, uint32_t srcLen, |
| void *destData, uint32_t *destLen) |
| { |
| TEE_Result res; |
| uint8_t *dst = destData; |
| size_t acc_dlen = 0; |
| uint64_t tmp_dlen; |
| size_t req_dlen; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (srcData == NULL && srcLen != 0) || |
| destLen == NULL || |
| (destData == NULL && *destLen != 0)) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_CIPHER) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * Check that the final block doesn't require padding for those |
| * algorithms that requires client to supply padding. |
| */ |
| if (operation->info.algorithm == TEE_ALG_AES_ECB_NOPAD || |
| operation->info.algorithm == TEE_ALG_AES_CBC_NOPAD || |
| operation->info.algorithm == TEE_ALG_DES_ECB_NOPAD || |
| operation->info.algorithm == TEE_ALG_DES_CBC_NOPAD || |
| operation->info.algorithm == TEE_ALG_DES3_ECB_NOPAD || |
| operation->info.algorithm == TEE_ALG_DES3_CBC_NOPAD || |
| operation->info.algorithm == TEE_ALG_SM4_ECB_NOPAD || |
| operation->info.algorithm == TEE_ALG_SM4_CBC_NOPAD) { |
| if (((operation->buffer_offs + srcLen) % operation->block_size) |
| != 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| } |
| |
| /* |
| * Check that required destLen is big enough before starting to feed |
| * data to the algorithm. Errors during feeding of data are fatal as we |
| * can't restore sync with this API. |
| */ |
| if (operation->block_size > 1) { |
| req_dlen = operation->buffer_offs + srcLen; |
| } else { |
| req_dlen = srcLen; |
| } |
| if (*destLen < req_dlen) { |
| *destLen = req_dlen; |
| res = TEE_ERROR_SHORT_BUFFER; |
| goto out; |
| } |
| |
| tmp_dlen = *destLen - acc_dlen; |
| if (operation->block_size > 1) { |
| res = tee_buffer_update(operation, utee_cipher_update, |
| srcData, srcLen, dst, &tmp_dlen); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| dst += tmp_dlen; |
| acc_dlen += tmp_dlen; |
| |
| tmp_dlen = *destLen - acc_dlen; |
| res = utee_cipher_final(operation->state, operation->buffer, |
| operation->buffer_offs, dst, &tmp_dlen); |
| } else { |
| res = utee_cipher_final(operation->state, srcData, |
| srcLen, dst, &tmp_dlen); |
| } |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| acc_dlen += tmp_dlen; |
| *destLen = acc_dlen; |
| |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_INITIALIZED; |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| /* Cryptographic Operations API - MAC Functions */ |
| |
| void TEE_MACInit(TEE_OperationHandle operation, const void *IV, uint32_t IVLen) |
| { |
| if (operation == TEE_HANDLE_NULL) |
| TEE_Panic(0); |
| |
| if (operation->info.operationClass != TEE_OPERATION_MAC) |
| TEE_Panic(0); |
| |
| if (!(operation->info.handleState & TEE_HANDLE_FLAG_KEY_SET) || |
| !(operation->key1)) |
| TEE_Panic(0); |
| |
| if (operation->operationState != TEE_OPERATION_STATE_INITIAL) |
| TEE_ResetOperation(operation); |
| |
| operation->operationState = TEE_OPERATION_STATE_ACTIVE; |
| |
| init_hash_operation(operation, IV, IVLen); |
| } |
| |
| void TEE_MACUpdate(TEE_OperationHandle operation, const void *chunk, |
| uint32_t chunkSize) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL || (chunk == NULL && chunkSize != 0)) |
| TEE_Panic(0); |
| |
| if (operation->info.operationClass != TEE_OPERATION_MAC) |
| TEE_Panic(0); |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) |
| TEE_Panic(0); |
| |
| if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) |
| TEE_Panic(0); |
| |
| res = utee_hash_update(operation->state, chunk, chunkSize); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| TEE_Result TEE_MACComputeFinal(TEE_OperationHandle operation, |
| const void *message, uint32_t messageLen, |
| void *mac, uint32_t *macLen) |
| { |
| TEE_Result res; |
| uint64_t ml; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (message == NULL && messageLen != 0) || |
| mac == NULL || |
| macLen == NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_MAC) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| ml = *macLen; |
| res = utee_hash_final(operation->state, message, messageLen, mac, &ml); |
| *macLen = ml; |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_INITIALIZED; |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_MACCompareFinal(TEE_OperationHandle operation, |
| const void *message, uint32_t messageLen, |
| const void *mac, uint32_t macLen) |
| { |
| TEE_Result res; |
| uint8_t computed_mac[TEE_MAX_HASH_SIZE]; |
| uint32_t computed_mac_size = TEE_MAX_HASH_SIZE; |
| |
| if (operation->info.operationClass != TEE_OPERATION_MAC) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| res = TEE_MACComputeFinal(operation, message, messageLen, computed_mac, |
| &computed_mac_size); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| if (computed_mac_size != macLen) { |
| res = TEE_ERROR_MAC_INVALID; |
| goto out; |
| } |
| |
| if (consttime_memcmp(mac, computed_mac, computed_mac_size) != 0) { |
| res = TEE_ERROR_MAC_INVALID; |
| goto out; |
| } |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_MAC_INVALID) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| /* Cryptographic Operations API - Authenticated Encryption Functions */ |
| |
| TEE_Result TEE_AEInit(TEE_OperationHandle operation, const void *nonce, |
| uint32_t nonceLen, uint32_t tagLen, uint32_t AADLen, |
| uint32_t payloadLen) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL || nonce == NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_AE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->operationState != TEE_OPERATION_STATE_INITIAL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * AES-CCM tag len is specified by AES-CCM spec and handled in TEE Core |
| * in the implementation. But AES-GCM spec doesn't specify the tag len |
| * according to the same principle so we have to check here instead to |
| * be GP compliant. |
| */ |
| if (operation->info.algorithm == TEE_ALG_AES_GCM) { |
| /* |
| * From GP spec: For AES-GCM, can be 128, 120, 112, 104, or 96 |
| */ |
| if (tagLen < 96 || tagLen > 128 || (tagLen % 8 != 0)) { |
| res = TEE_ERROR_NOT_SUPPORTED; |
| goto out; |
| } |
| } |
| |
| res = utee_authenc_init(operation->state, nonce, nonceLen, |
| tagLen / 8, AADLen, payloadLen); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| operation->ae_tag_len = tagLen / 8; |
| operation->info.handleState |= TEE_HANDLE_FLAG_INITIALIZED; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_NOT_SUPPORTED) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| void TEE_AEUpdateAAD(TEE_OperationHandle operation, const void *AADdata, |
| uint32_t AADdataLen) |
| { |
| TEE_Result res; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (AADdata == NULL && AADdataLen != 0)) |
| TEE_Panic(0); |
| |
| if (operation->info.operationClass != TEE_OPERATION_AE) |
| TEE_Panic(0); |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) |
| TEE_Panic(0); |
| |
| res = utee_authenc_update_aad(operation->state, AADdata, AADdataLen); |
| |
| operation->operationState = TEE_OPERATION_STATE_ACTIVE; |
| |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| TEE_Result TEE_AEUpdate(TEE_OperationHandle operation, const void *srcData, |
| uint32_t srcLen, void *destData, uint32_t *destLen) |
| { |
| TEE_Result res; |
| size_t req_dlen; |
| uint64_t dl; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (srcData == NULL && srcLen != 0) || |
| destLen == NULL || |
| (destData == NULL && *destLen != 0)) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_AE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (!srcData && !srcLen) { |
| *destLen = 0; |
| res = TEE_SUCCESS; |
| goto out; |
| } |
| |
| /* |
| * Check that required destLen is big enough before starting to feed |
| * data to the algorithm. Errors during feeding of data are fatal as we |
| * can't restore sync with this API. |
| */ |
| if (operation->block_size > 1) { |
| req_dlen = ROUNDDOWN(operation->buffer_offs + srcLen, |
| operation->block_size); |
| } else { |
| req_dlen = srcLen; |
| } |
| |
| if (*destLen < req_dlen) { |
| *destLen = req_dlen; |
| res = TEE_ERROR_SHORT_BUFFER; |
| goto out; |
| } |
| |
| dl = *destLen; |
| if (operation->block_size > 1) { |
| res = tee_buffer_update(operation, utee_authenc_update_payload, |
| srcData, srcLen, destData, &dl); |
| } else { |
| if (srcLen > 0) { |
| res = utee_authenc_update_payload(operation->state, |
| srcData, srcLen, |
| destData, &dl); |
| } else { |
| dl = 0; |
| res = TEE_SUCCESS; |
| } |
| } |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| *destLen = dl; |
| |
| operation->operationState = TEE_OPERATION_STATE_ACTIVE; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_AEEncryptFinal(TEE_OperationHandle operation, |
| const void *srcData, uint32_t srcLen, |
| void *destData, uint32_t *destLen, void *tag, |
| uint32_t *tagLen) |
| { |
| TEE_Result res; |
| uint8_t *dst = destData; |
| size_t acc_dlen = 0; |
| uint64_t tmp_dlen; |
| size_t req_dlen; |
| uint64_t tl; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (srcData == NULL && srcLen != 0) || |
| destLen == NULL || |
| (destData == NULL && *destLen != 0) || |
| tag == NULL || tagLen == NULL) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_AE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * Check that required destLen is big enough before starting to feed |
| * data to the algorithm. Errors during feeding of data are fatal as we |
| * can't restore sync with this API. |
| * |
| * Need to check this before update_payload since sync would be lost if |
| * we return short buffer after that. |
| */ |
| res = TEE_ERROR_GENERIC; |
| |
| req_dlen = operation->buffer_offs + srcLen; |
| if (*destLen < req_dlen) { |
| *destLen = req_dlen; |
| res = TEE_ERROR_SHORT_BUFFER; |
| } |
| |
| if (*tagLen < operation->ae_tag_len) { |
| *tagLen = operation->ae_tag_len; |
| res = TEE_ERROR_SHORT_BUFFER; |
| } |
| |
| if (res == TEE_ERROR_SHORT_BUFFER) |
| goto out; |
| |
| tl = *tagLen; |
| tmp_dlen = *destLen - acc_dlen; |
| if (operation->block_size > 1) { |
| res = tee_buffer_update(operation, utee_authenc_update_payload, |
| srcData, srcLen, dst, &tmp_dlen); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| dst += tmp_dlen; |
| acc_dlen += tmp_dlen; |
| |
| tmp_dlen = *destLen - acc_dlen; |
| res = utee_authenc_enc_final(operation->state, |
| operation->buffer, |
| operation->buffer_offs, dst, |
| &tmp_dlen, tag, &tl); |
| } else { |
| res = utee_authenc_enc_final(operation->state, srcData, |
| srcLen, dst, &tmp_dlen, |
| tag, &tl); |
| } |
| *tagLen = tl; |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| acc_dlen += tmp_dlen; |
| *destLen = acc_dlen; |
| |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_INITIALIZED; |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_AEDecryptFinal(TEE_OperationHandle operation, |
| const void *srcData, uint32_t srcLen, |
| void *destData, uint32_t *destLen, void *tag, |
| uint32_t tagLen) |
| { |
| TEE_Result res; |
| uint8_t *dst = destData; |
| size_t acc_dlen = 0; |
| uint64_t tmp_dlen; |
| size_t req_dlen; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (srcData == NULL && srcLen != 0) || |
| destLen == NULL || |
| (destData == NULL && *destLen != 0) || |
| (tag == NULL && tagLen != 0)) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if (operation->info.operationClass != TEE_OPERATION_AE) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { |
| res = TEE_ERROR_BAD_PARAMETERS; |
| goto out; |
| } |
| |
| /* |
| * Check that required destLen is big enough before starting to feed |
| * data to the algorithm. Errors during feeding of data are fatal as we |
| * can't restore sync with this API. |
| */ |
| req_dlen = operation->buffer_offs + srcLen; |
| if (*destLen < req_dlen) { |
| *destLen = req_dlen; |
| res = TEE_ERROR_SHORT_BUFFER; |
| goto out; |
| } |
| |
| tmp_dlen = *destLen - acc_dlen; |
| if (operation->block_size > 1) { |
| res = tee_buffer_update(operation, utee_authenc_update_payload, |
| srcData, srcLen, dst, &tmp_dlen); |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| dst += tmp_dlen; |
| acc_dlen += tmp_dlen; |
| |
| tmp_dlen = *destLen - acc_dlen; |
| res = utee_authenc_dec_final(operation->state, |
| operation->buffer, |
| operation->buffer_offs, dst, |
| &tmp_dlen, tag, tagLen); |
| } else { |
| res = utee_authenc_dec_final(operation->state, srcData, |
| srcLen, dst, &tmp_dlen, |
| tag, tagLen); |
| } |
| if (res != TEE_SUCCESS) |
| goto out; |
| |
| /* Supplied tagLen should match what we initiated with */ |
| if (tagLen != operation->ae_tag_len) |
| res = TEE_ERROR_MAC_INVALID; |
| |
| acc_dlen += tmp_dlen; |
| *destLen = acc_dlen; |
| |
| operation->info.handleState &= ~TEE_HANDLE_FLAG_INITIALIZED; |
| |
| operation->operationState = TEE_OPERATION_STATE_INITIAL; |
| |
| out: |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER && |
| res != TEE_ERROR_MAC_INVALID) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| /* Cryptographic Operations API - Asymmetric Functions */ |
| |
| TEE_Result TEE_AsymmetricEncrypt(TEE_OperationHandle operation, |
| const TEE_Attribute *params, |
| uint32_t paramCount, const void *srcData, |
| uint32_t srcLen, void *destData, |
| uint32_t *destLen) |
| { |
| TEE_Result res; |
| struct utee_attribute ua[paramCount]; |
| uint64_t dl; |
| |
| if (operation == TEE_HANDLE_NULL || (srcData == NULL && srcLen != 0) || |
| destLen == NULL || (destData == NULL && *destLen != 0)) |
| TEE_Panic(0); |
| if (params == NULL && paramCount != 0) |
| TEE_Panic(0); |
| if (!operation->key1) |
| TEE_Panic(0); |
| if (operation->info.operationClass != TEE_OPERATION_ASYMMETRIC_CIPHER) |
| TEE_Panic(0); |
| if (operation->info.mode != TEE_MODE_ENCRYPT) |
| TEE_Panic(0); |
| |
| __utee_from_attr(ua, params, paramCount); |
| dl = *destLen; |
| res = utee_asymm_operate(operation->state, ua, paramCount, srcData, |
| srcLen, destData, &dl); |
| *destLen = dl; |
| |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER && |
| res != TEE_ERROR_BAD_PARAMETERS) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_AsymmetricDecrypt(TEE_OperationHandle operation, |
| const TEE_Attribute *params, |
| uint32_t paramCount, const void *srcData, |
| uint32_t srcLen, void *destData, |
| uint32_t *destLen) |
| { |
| TEE_Result res; |
| struct utee_attribute ua[paramCount]; |
| uint64_t dl; |
| |
| if (operation == TEE_HANDLE_NULL || (srcData == NULL && srcLen != 0) || |
| destLen == NULL || (destData == NULL && *destLen != 0)) |
| TEE_Panic(0); |
| if (params == NULL && paramCount != 0) |
| TEE_Panic(0); |
| if (!operation->key1) |
| TEE_Panic(0); |
| if (operation->info.operationClass != TEE_OPERATION_ASYMMETRIC_CIPHER) |
| TEE_Panic(0); |
| if (operation->info.mode != TEE_MODE_DECRYPT) |
| TEE_Panic(0); |
| |
| __utee_from_attr(ua, params, paramCount); |
| dl = *destLen; |
| res = utee_asymm_operate(operation->state, ua, paramCount, srcData, |
| srcLen, destData, &dl); |
| *destLen = dl; |
| |
| if (res != TEE_SUCCESS && |
| res != TEE_ERROR_SHORT_BUFFER && |
| res != TEE_ERROR_BAD_PARAMETERS) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_AsymmetricSignDigest(TEE_OperationHandle operation, |
| const TEE_Attribute *params, |
| uint32_t paramCount, const void *digest, |
| uint32_t digestLen, void *signature, |
| uint32_t *signatureLen) |
| { |
| TEE_Result res; |
| struct utee_attribute ua[paramCount]; |
| uint64_t sl; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (digest == NULL && digestLen != 0) || |
| signature == NULL || signatureLen == NULL) |
| TEE_Panic(0); |
| if (params == NULL && paramCount != 0) |
| TEE_Panic(0); |
| if (!operation->key1) |
| TEE_Panic(0); |
| if (operation->info.operationClass != |
| TEE_OPERATION_ASYMMETRIC_SIGNATURE) |
| TEE_Panic(0); |
| if (operation->info.mode != TEE_MODE_SIGN) |
| TEE_Panic(0); |
| |
| __utee_from_attr(ua, params, paramCount); |
| sl = *signatureLen; |
| res = utee_asymm_operate(operation->state, ua, paramCount, digest, |
| digestLen, signature, &sl); |
| *signatureLen = sl; |
| |
| if (res != TEE_SUCCESS && res != TEE_ERROR_SHORT_BUFFER) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| TEE_Result TEE_AsymmetricVerifyDigest(TEE_OperationHandle operation, |
| const TEE_Attribute *params, |
| uint32_t paramCount, const void *digest, |
| uint32_t digestLen, |
| const void *signature, |
| uint32_t signatureLen) |
| { |
| TEE_Result res; |
| struct utee_attribute ua[paramCount]; |
| |
| if (operation == TEE_HANDLE_NULL || |
| (digest == NULL && digestLen != 0) || |
| (signature == NULL && signatureLen != 0)) |
| TEE_Panic(0); |
| if (params == NULL && paramCount != 0) |
| TEE_Panic(0); |
| if (!operation->key1) |
| TEE_Panic(0); |
| if (operation->info.operationClass != |
| TEE_OPERATION_ASYMMETRIC_SIGNATURE) |
| TEE_Panic(0); |
| if (operation->info.mode != TEE_MODE_VERIFY) |
| TEE_Panic(0); |
| |
| __utee_from_attr(ua, params, paramCount); |
| res = utee_asymm_verify(operation->state, ua, paramCount, digest, |
| digestLen, signature, signatureLen); |
| |
| if (res != TEE_SUCCESS && res != TEE_ERROR_SIGNATURE_INVALID) |
| TEE_Panic(res); |
| |
| return res; |
| } |
| |
| /* Cryptographic Operations API - Key Derivation Functions */ |
| |
| void TEE_DeriveKey(TEE_OperationHandle operation, |
| const TEE_Attribute *params, uint32_t paramCount, |
| TEE_ObjectHandle derivedKey) |
| { |
| TEE_Result res; |
| TEE_ObjectInfo key_info; |
| struct utee_attribute ua[paramCount]; |
| |
| if (operation == TEE_HANDLE_NULL || derivedKey == 0) |
| TEE_Panic(0); |
| if (params == NULL && paramCount != 0) |
| TEE_Panic(0); |
| if (TEE_ALG_GET_CLASS(operation->info.algorithm) != |
| TEE_OPERATION_KEY_DERIVATION) |
| TEE_Panic(0); |
| |
| if (operation->info.operationClass != TEE_OPERATION_KEY_DERIVATION) |
| TEE_Panic(0); |
| if (!operation->key1) |
| TEE_Panic(0); |
| if (operation->info.mode != TEE_MODE_DERIVE) |
| TEE_Panic(0); |
| if ((operation->info.handleState & TEE_HANDLE_FLAG_KEY_SET) == 0) |
| TEE_Panic(0); |
| |
| res = utee_cryp_obj_get_info((unsigned long)derivedKey, &key_info); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| |
| if (key_info.objectType != TEE_TYPE_GENERIC_SECRET) |
| TEE_Panic(0); |
| if ((key_info.handleFlags & TEE_HANDLE_FLAG_INITIALIZED) != 0) |
| TEE_Panic(0); |
| |
| __utee_from_attr(ua, params, paramCount); |
| res = utee_cryp_derive_key(operation->state, ua, paramCount, |
| (unsigned long)derivedKey); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| /* Cryptographic Operations API - Random Number Generation Functions */ |
| |
| void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen) |
| { |
| TEE_Result res; |
| |
| res = utee_cryp_random_number_generate(randomBuffer, randomBufferLen); |
| if (res != TEE_SUCCESS) |
| TEE_Panic(res); |
| } |
| |
| int rand(void) |
| { |
| int rc; |
| |
| TEE_GenerateRandom(&rc, sizeof(rc)); |
| |
| /* |
| * RAND_MAX is the larges int, INT_MAX which is all bits but the |
| * highest bit set. |
| */ |
| return rc & RAND_MAX; |
| } |