* This file is part of the openHiTLS project.
*
* openHiTLS is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "hitls_build.h"
#ifdef HITLS_CRYPTO_PROVIDER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crypt_utils.h"
#include "crypt_errno.h"
#include "bsl_sal.h"
#include "bsl_list.h"
#include "bsl_err_internal.h"
#include "crypt_eal_provider.h"
#include "crypt_eal_implprovider.h"
#include "crypt_provider_local.h"
#include "crypt_provider.h"
#include "eal_drbg_local.h"
#define PROVIDER_INIT_FUNC "CRYPT_EAL_ProviderInit"
BSL_SAL_DECLARE_THREAD_ONCE(g_threadRunOnce);
CRYPT_EAL_LibCtx *CRYPT_EAL_LibCtxNew(void)
{
return CRYPT_EAL_LibCtxNewInternal();
}
void CRYPT_EAL_LibCtxFree(CRYPT_EAL_LibCtx *libCtx)
{
if (libCtx == NULL) {
return;
}
#ifdef HITLS_CRYPTO_DRBG
if (libCtx->drbg != NULL) {
EAL_RandDeinit(libCtx->drbg);
libCtx->drbg = NULL;
}
#endif
if (libCtx->providers != NULL) {
BSL_LIST_FREE(libCtx->providers, (BSL_LIST_PFUNC_FREE)CRYPT_EAL_ProviderMgrCtxFree);
}
if (libCtx->lock != NULL) {
BSL_SAL_ThreadLockFree(libCtx->lock);
}
#ifdef HITLS_BSL_SAL_DL
BSL_SAL_FREE(libCtx->searchProviderPath);
#endif
BSL_SAL_Free(libCtx);
}
static void InitPreDefinedProviders(void)
{
CRYPT_EAL_LibCtx *globalCtx = CRYPT_EAL_GetGlobalLibCtx();
if (globalCtx == NULL) {
int32_t ret = CRYPT_EAL_InitPreDefinedProviders();
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
}
}
}
CRYPT_EAL_LibCtx *GetCurrentProviderLibCtx(CRYPT_EAL_LibCtx *libCtx)
{
CRYPT_EAL_LibCtx *curLibCtx = libCtx;
if (curLibCtx == NULL) {
int32_t ret = BSL_SAL_ThreadRunOnce(&g_threadRunOnce, InitPreDefinedProviders);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return NULL;
}
curLibCtx = CRYPT_EAL_GetGlobalLibCtx();
}
return curLibCtx;
}
static int32_t ListCompareProvider(const void *a, const void *b)
{
const CRYPT_EAL_ProvMgrCtx *ctx = (const CRYPT_EAL_ProvMgrCtx *)a;
const char *providerName = (const char *)b;
return (strcmp(ctx->providerName, providerName) == 0) ? 0 : 1;
}
static int32_t CheckProviderLoadedNoLock(CRYPT_EAL_LibCtx *libCtx, const char *providerName, bool increaseRef,
CRYPT_EAL_ProvMgrCtx **providerMgr)
{
CRYPT_EAL_ProvMgrCtx *tempProviderMgr =
(CRYPT_EAL_ProvMgrCtx *)BSL_LIST_SearchDataConst(libCtx->providers, providerName, ListCompareProvider, NULL);
if (tempProviderMgr != NULL && increaseRef) {
int32_t tempCount = 0;
int32_t ret = BSL_SAL_AtomicUpReferences(&tempProviderMgr->ref, &tempCount);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
}
*providerMgr = tempProviderMgr;
return CRYPT_SUCCESS;
}
static int32_t CheckProviderLoaded(CRYPT_EAL_LibCtx *libCtx, const char *providerName, bool increaseRef,
CRYPT_EAL_ProvMgrCtx **providerMgr)
{
int32_t ret = BSL_SAL_ThreadReadLock(libCtx->lock);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = CheckProviderLoadedNoLock(libCtx, providerName, increaseRef, providerMgr);
(void)BSL_SAL_ThreadUnlock(libCtx->lock);
return ret;
}
static bool IsEalPreDefinedProvider(const char *providerName)
{
const char *preProvider[] = {CRYPT_EAL_DEFAULT_PROVIDER};
for (size_t i = 0; i < sizeof(preProvider) / sizeof(preProvider[0]); i++) {
if (strcmp(preProvider[i], providerName) == 0) {
return true;
}
}
return false;
}
#ifdef HITLS_BSL_SAL_DL
static int32_t FindProviderInitFunc(CRYPT_EAL_LibCtx *libCtx, char *providerName, void **initFunc, void **handle)
{
uint32_t pathLen = BSL_SAL_Strnlen(providerName, DEFAULT_PROVIDER_NAME_LEN_MAX) +
BSL_SAL_Strnlen(libCtx->searchProviderPath, DEFAULT_PROVIDER_PATH_LEN_MAX) + 1;
if (pathLen > DEFAULT_PROVIDER_PATH_LEN_MAX) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
char *providerPath = providerName;
int32_t ret;
if (libCtx->searchProviderPath != NULL) {
providerPath = (char *)BSL_SAL_Calloc(pathLen + 1, sizeof(char));
if (providerPath == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ret = snprintf(providerPath, pathLen + 1, "%s/%s", libCtx->searchProviderPath, providerName);
if (ret < 0 || (size_t)ret >= pathLen + 1) {
BSL_SAL_Free(providerPath);
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
}
void *handleTemp = NULL;
ret = BSL_SAL_LoadLib(providerPath, &handleTemp);
if (libCtx->searchProviderPath != NULL) {
BSL_SAL_Free(providerPath);
}
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = BSL_SAL_GetFuncAddress(handleTemp, PROVIDER_INIT_FUNC, initFunc);
if (ret != BSL_SUCCESS) {
(void)BSL_SAL_UnLoadLib(handleTemp);
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
*handle = handleTemp;
return BSL_SUCCESS;
}
#endif
int32_t CRYPT_EAL_ProviderLoad(CRYPT_EAL_LibCtx *libCtx, BSL_SAL_LibFmtCmd cmd,
const char *providerName, BSL_Param *param, CRYPT_EAL_ProvMgrCtx **mgrCtx)
{
if (providerName == NULL || (mgrCtx != NULL && *mgrCtx != NULL)) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
void *handle = NULL;
CRYPT_EAL_LibCtx *localCtx = GetCurrentProviderLibCtx(libCtx);
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
CRYPT_EAL_ProvMgrCtx *providerMgr = NULL;
char *providerFullName = NULL;
int32_t ret = BSL_SAL_LibNameFormat(cmd, providerName, &providerFullName);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = BSL_SAL_ThreadWriteLock(localCtx->lock);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
BSL_SAL_Free(providerFullName);
return ret;
}
ret = CheckProviderLoadedNoLock(localCtx, providerFullName, true, &providerMgr);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
if (providerMgr != NULL) {
if (mgrCtx != NULL) {
*mgrCtx = providerMgr;
}
ret = CRYPT_SUCCESS;
goto EXIT;
}
CRYPT_EAL_ImplProviderInit initFunc = NULL;
if (IsEalPreDefinedProvider(providerFullName)) {
initFunc = CRYPT_EAL_DefaultProvInit;
} else {
#ifdef HITLS_BSL_SAL_DL
ret = FindProviderInitFunc(localCtx, providerFullName, (void **)&initFunc, &handle);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
#else
ret = CRYPT_NOT_SUPPORT;
goto EXIT;
#endif
}
#ifndef HITLS_BSL_SAL_DL
ret = CRYPT_EAL_AddNewProvMgrCtx(localCtx, providerFullName, NULL, initFunc, handle, param, mgrCtx);
#else
ret = CRYPT_EAL_AddNewProvMgrCtx(localCtx, providerFullName, localCtx->searchProviderPath, initFunc, handle, param,
mgrCtx);
if (ret != CRYPT_SUCCESS) {
(void)BSL_SAL_UnLoadLib(handle);
}
#endif
EXIT:
(void)BSL_SAL_ThreadUnlock(localCtx->lock);
BSL_SAL_Free(providerFullName);
return ret;
}
int32_t CRYPT_EAL_ProviderRegister(CRYPT_EAL_LibCtx *libCtx, const char *providerName, CRYPT_EAL_ImplProviderInit init,
BSL_Param *param, CRYPT_EAL_ProvMgrCtx **mgrCtx)
{
if (providerName == NULL || (mgrCtx != NULL && *mgrCtx != NULL)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint32_t providerNameLen = BSL_SAL_Strnlen(providerName, DEFAULT_PROVIDER_NAME_LEN_MAX + 1);
if (providerNameLen == DEFAULT_PROVIDER_NAME_LEN_MAX + 1 || providerNameLen == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
CRYPT_EAL_ProvMgrCtx *providerMgr = NULL;
CRYPT_EAL_ImplProviderInit initFunc = init;
CRYPT_EAL_LibCtx *localCtx = GetCurrentProviderLibCtx(libCtx);
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
int32_t ret = BSL_SAL_ThreadWriteLock(localCtx->lock);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = CheckProviderLoadedNoLock(localCtx, providerName, true, &providerMgr);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
if (providerMgr != NULL) {
if (mgrCtx != NULL) {
*mgrCtx = providerMgr;
}
ret = CRYPT_SUCCESS;
goto EXIT;
}
if (IsEalPreDefinedProvider(providerName)) {
initFunc = CRYPT_EAL_DefaultProvInit;
} else {
if (initFunc == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
ret = CRYPT_NULL_INPUT;
goto EXIT;
}
}
ret = CRYPT_EAL_AddNewProvMgrCtx(localCtx, providerName, NULL, initFunc, NULL, param, mgrCtx);
EXIT:
(void)BSL_SAL_ThreadUnlock(localCtx->lock);
return ret;
}
int32_t CRYPT_EAL_ProviderIsLoaded(CRYPT_EAL_LibCtx *libCtx, BSL_SAL_LibFmtCmd cmd, const char *providerName,
bool *isLoaded)
{
#ifdef HITLS_BSL_SAL_DL
if (providerName == NULL || isLoaded == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
CRYPT_EAL_LibCtx *localCtx = GetCurrentProviderLibCtx(libCtx);
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
char *providerFullName = NULL;
int32_t ret = BSL_SAL_LibNameFormat(cmd, providerName, &providerFullName);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
CRYPT_EAL_ProvMgrCtx *providerMgr = NULL;
ret = CheckProviderLoaded(localCtx, providerFullName, false, &providerMgr);
BSL_SAL_Free(providerFullName);
if (ret != CRYPT_SUCCESS) {
return ret;
}
*isLoaded = providerMgr != NULL ? true : false;
return CRYPT_SUCCESS;
#else
(void)libCtx;
(void)cmd;
(void)providerName;
(void)isLoaded;
return CRYPT_NOT_SUPPORT;
#endif
}
static void RemoveAndFreeProvider(BslList *providers, CRYPT_EAL_ProvMgrCtx *providerMgr)
{
BslListNode *node = BSL_LIST_FirstNode(providers);
while (node != NULL) {
if (BSL_LIST_GetData(node) == providerMgr) {
BSL_LIST_DetachNode(providers, &node);
break;
}
node = BSL_LIST_GetNextNode(providers, node);
}
CRYPT_EAL_ProviderMgrCtxFree(providerMgr);
}
int32_t CRYPT_EAL_ProviderUnload(CRYPT_EAL_LibCtx *libCtx, BSL_SAL_LibFmtCmd cmd, const char *providerName)
{
RETURN_RET_IF(providerName == NULL, CRYPT_NULL_INPUT);
CRYPT_EAL_LibCtx *localCtx = libCtx;
if (localCtx == NULL) {
localCtx = CRYPT_EAL_GetGlobalLibCtx();
}
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
char *providerFullName = NULL;
int32_t ret = BSL_SAL_LibNameFormat(cmd, providerName, &providerFullName);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = BSL_SAL_ThreadWriteLock(localCtx->lock);
if (ret != BSL_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
BSL_SAL_FREE(providerFullName);
return ret;
}
CRYPT_EAL_ProvMgrCtx *providerMgr = (CRYPT_EAL_ProvMgrCtx *)BSL_LIST_SearchDataConst(localCtx->providers,
providerFullName, ListCompareProvider, NULL);
BSL_SAL_FREE(providerFullName);
if (providerMgr == NULL) {
(void)BSL_SAL_ThreadUnlock(localCtx->lock);
return CRYPT_SUCCESS;
}
int refCount = 0;
ret = BSL_SAL_AtomicDownReferences(&providerMgr->ref, &refCount);
if (ret != BSL_SUCCESS) {
(void)BSL_SAL_ThreadUnlock(localCtx->lock);
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
if (refCount <= 0) {
RemoveAndFreeProvider(localCtx->providers, providerMgr);
}
(void)BSL_SAL_ThreadUnlock(localCtx->lock);
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_ProviderSetLoadPath(CRYPT_EAL_LibCtx *libCtx, const char *searchPath)
{
#ifdef HITLS_BSL_SAL_DL
if (BSL_SAL_Strnlen(searchPath, DEFAULT_PROVIDER_PATH_LEN_MAX) >= DEFAULT_PROVIDER_PATH_LEN_MAX) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
CRYPT_EAL_LibCtx *localCtx = GetCurrentProviderLibCtx(libCtx);
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
char *tempPath = NULL;
if (searchPath != NULL) {
tempPath = BSL_SAL_Dump(searchPath, BSL_SAL_Strnlen(searchPath, DEFAULT_PROVIDER_PATH_LEN_MAX) + 1);
if (tempPath == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
}
BSL_SAL_FREE(localCtx->searchProviderPath);
localCtx->searchProviderPath = tempPath;
return CRYPT_SUCCESS;
#else
(void)libCtx;
(void)searchPath;
return CRYPT_NOT_SUPPORT;
#endif
}
static int32_t GetProviderUserCtx(CRYPT_EAL_ProvMgrCtx *ctx, void **val)
{
if (val == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
*val = ctx->provCtx;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_ProviderCtrl(CRYPT_EAL_ProvMgrCtx *ctx, int32_t cmd, void *val, uint32_t valLen)
{
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (cmd == CRYPT_PROVIDER_GET_USER_CTX) {
return GetProviderUserCtx(ctx, val);
}
if (ctx->provCtrlCb == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_NOT_SUPPORT);
return CRYPT_PROVIDER_NOT_SUPPORT;
}
return ctx->provCtrlCb(ctx->provCtx, cmd, val, valLen);
}
int32_t CRYPT_EAL_ProviderGetCaps(CRYPT_EAL_ProvMgrCtx *ctx, int32_t cmd, CRYPT_EAL_ProcessFuncCb cb, void *args)
{
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->provGetCap == NULL) {
return CRYPT_SUCCESS;
}
return ctx->provGetCap(ctx->provCtx, cmd, cb, args);
}
int32_t CRYPT_EAL_ProviderProcessAll(CRYPT_EAL_LibCtx *ctx, CRYPT_EAL_ProviderProcessCb cb, void *args)
{
if (cb == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
CRYPT_EAL_LibCtx *localCtx = GetCurrentProviderLibCtx(ctx);
if (localCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_INVALID_LIB_CTX);
return CRYPT_PROVIDER_INVALID_LIB_CTX;
}
BslListNode *node = BSL_LIST_FirstNode(localCtx->providers);
while (node != NULL) {
CRYPT_EAL_ProvMgrCtx *providerMgr = (CRYPT_EAL_ProvMgrCtx *)BSL_LIST_GetData(node);
int32_t ret = cb(providerMgr, args);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
node = BSL_LIST_GetNextNode(localCtx->providers, node);
}
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_ProviderQuery(CRYPT_EAL_ProvMgrCtx *ctx, int32_t operaId, CRYPT_EAL_AlgInfo **algInfos)
{
if (ctx == NULL || algInfos == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->provQueryCb == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_PROVIDER_NOT_SUPPORT);
return CRYPT_PROVIDER_NOT_SUPPORT;
}
return ctx->provQueryCb(ctx->provCtx, operaId, algInfos);
}
#endif