* 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_ENTROPY
#include <stdint.h>
#include <string.h>
#include "bsl_err_internal.h"
#include "crypt_errno.h"
#include "entropy_seed_pool.h"
#define SEEDPOOL_ES_MAX_SIZE 16
#define SEEDPOOL_ES_INIT_MINENTROPY 9
#define SEEDPOOL_ES_FULL_MINENTROPY 8
#define SEEDPOOL_ES_SYS_MINENTROPY 7
ENTROPY_SeedPool *ENTROPY_SeedPoolNew(bool isCreateNullPool)
{
ENTROPY_SeedPool *poolCtx = BSL_SAL_Malloc(sizeof(ENTROPY_SeedPool));
if (poolCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
memset(poolCtx, 0, sizeof(ENTROPY_SeedPool));
poolCtx->esList = BSL_LIST_New(sizeof(ENTROPY_Source));
if (poolCtx->esList == NULL) {
BSL_SAL_Free(poolCtx);
BSL_ERR_PUSH_ERROR(BSL_LIST_MALLOC_FAIL);
return NULL;
}
poolCtx->minEntropy = SEEDPOOL_ES_INIT_MINENTROPY;
if (isCreateNullPool) {
return poolCtx;
}
CRYPT_EAL_EsPara para = {false, SEEDPOOL_ES_SYS_MINENTROPY, NULL, ENTROPY_SysEntropyGet};
int32_t ret = ENTROPY_SeedPoolAddEs(poolCtx, ¶);
if (ret != CRYPT_SUCCESS) {
ENTROPY_SeedPoolFree(poolCtx);
BSL_ERR_PUSH_ERROR(ret);
return NULL;
}
CRYPT_EAL_EsPara hwPara = {true, SEEDPOOL_ES_FULL_MINENTROPY, NULL, ENTROPY_HWEntropyGet};
ret = ENTROPY_SeedPoolAddEs(poolCtx, &hwPara);
if (ret != CRYPT_SUCCESS) {
ENTROPY_SeedPoolFree(poolCtx);
BSL_ERR_PUSH_ERROR(ret);
return NULL;
}
return poolCtx;
}
static ENTROPY_Source *SeedPoolEsNew(const CRYPT_EAL_EsPara *para)
{
ENTROPY_Source *es = BSL_SAL_Malloc(sizeof(ENTROPY_Source));
if (es == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
es->isPhysical = para->isPhysical;
es->minEntropy = para->minEntropy;
es->ctx = para->entropyCtx;
es->entropyGet = (EntropyGet)(para->entropyGet);
return es;
}
int32_t ENTROPY_SeedPoolAddEs(ENTROPY_SeedPool *pool, const CRYPT_EAL_EsPara *para)
{
if (pool == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (BSL_LIST_COUNT(pool->esList) >= SEEDPOOL_ES_MAX_SIZE) {
BSL_ERR_PUSH_ERROR(CRYPT_SEED_POOL_ES_LIST_FULL);
return CRYPT_SEED_POOL_ES_LIST_FULL;
}
ENTROPY_Source *es = SeedPoolEsNew(para);
if (es == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_SEED_POOL_NEW_ERROR);
return CRYPT_SEED_POOL_NEW_ERROR;
}
* The header insertion method is used to add an entropy source to ensure that the entropy source added by the
* invoker is used first when the entropy data is obtained.
*/
int32_t ret = BSL_LIST_AddElement(pool->esList, es, BSL_LIST_POS_BEGIN);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_Free(es);
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
pool->minEntropy = (pool->minEntropy < para->minEntropy) ? pool->minEntropy : para->minEntropy;
pool->isContainFes = pool->isContainFes || (para->minEntropy == SEEDPOOL_ES_FULL_MINENTROPY);
pool->isContainPes = pool->isContainPes || para->isPhysical;
return CRYPT_SUCCESS;
}
void ENTROPY_SeedPoolFree(ENTROPY_SeedPool *pool)
{
if (pool == NULL) {
return;
}
if (pool->esList != NULL) {
BSL_LIST_FREE(pool->esList, BSL_SAL_Free);
}
BSL_SAL_Free(pool);
return;
}
static uint32_t GetMinLen(uint32_t needEntropy, uint32_t currEntropy, uint32_t minEntropy, uint32_t bufLen)
{
if (needEntropy == 0) {
return bufLen;
}
uint32_t len =
(uint32_t)(((uint64_t)(needEntropy - currEntropy) + (uint64_t)minEntropy - 1) / (uint64_t)minEntropy);
return bufLen >= len ? len : bufLen;
}
uint32_t ENTROPY_SeedPoolCollect(ENTROPY_SeedPool *pool, bool isNpesUsed, uint32_t needEntropy, uint8_t *data,
uint32_t *len)
{
if (data == NULL || len == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return 0;
}
if (!ENTROPY_SeedPoolCheckState(pool, isNpesUsed)) {
BSL_ERR_PUSH_ERROR(CRYPT_SEED_POOL_STATE_ERROR);
return 0;
}
uint32_t bufLen = *len;
uint8_t *buf = data;
uint32_t curEntropy = 0;
for (BslListNode *node = BSL_LIST_FirstNode(pool->esList); node != NULL;
node = BSL_LIST_GetNextNode(pool->esList, node)) {
ENTROPY_Source *es = BSL_LIST_GetData(node);
if (!isNpesUsed && !es->isPhysical) {
continue;
}
uint32_t tmpLen = GetMinLen(needEntropy, curEntropy, es->minEntropy, bufLen);
uint32_t readLen = es->entropyGet(es->ctx, buf, tmpLen);
if (readLen > 0) {
bufLen -= readLen;
buf += readLen;
curEntropy += es->minEntropy * readLen;
}
bool flag = (needEntropy == 0) ? (bufLen == 0) : (curEntropy >= needEntropy);
if (flag) {
break;
}
}
*len = buf - data;
return curEntropy;
}
bool ENTROPY_SeedPoolCheckState(ENTROPY_SeedPool *seedPool, bool isNpesUsed)
{
if (seedPool == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return false;
}
if (BSL_LIST_COUNT(seedPool->esList) == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_SEED_POOL_NO_ENTROPY_SOURCE);
return false;
}
if (!isNpesUsed && !seedPool->isContainPes) {
BSL_ERR_PUSH_ERROR(CRYPT_SEED_POOL_NO_ENTROPY_SOURCE);
return false;
}
return true;
}
uint32_t ENTROPY_SeedPoolGetMinEntropy(ENTROPY_SeedPool *seedPool)
{
if (seedPool == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return 0;
}
return seedPool->minEntropy;
}
#endif