* 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"
#if defined(HITLS_CRYPTO_ENTROPY) && defined(HITLS_CRYPTO_ENTROPY_SYS)
#include <stdint.h>
#include <string.h>
#include "bsl_err_internal.h"
#include "bsl_list.h"
#include "crypt_errno.h"
#include "crypt_entropy.h"
#include "crypt_eal_entropy.h"
#include "es_entropy_pool.h"
#include "es_cf.h"
#include "es_noise_source.h"
struct ES_Entropy {
bool isWork;
bool enableTest;
uint32_t poolSize;
ES_EntropyPool *pool;
ES_CfMethod *cfMeth;
BslList *nsList;
CRYPT_EAL_EsLogFunc runLog;
};
#define ENTROPY_POOL_SIZE_DEFAULT 4096
#define ENTROPY_POOL_SIZE_MIN 512
#define ENTROPY_POOL_SIZE_MAX 4096
ENTROPY_EntropySource *ENTROPY_EsNew(void)
{
ENTROPY_EntropySource *es = BSL_SAL_Malloc(sizeof(ENTROPY_EntropySource));
if (es == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
memset(es, 0, sizeof(ENTROPY_EntropySource));
es->nsList = ES_NsListCreat();
if (es->nsList == NULL) {
BSL_SAL_Free(es);
return NULL;
}
es->poolSize = ENTROPY_POOL_SIZE_DEFAULT;
es->enableTest = false;
return es;
}
void ENTROPY_EsFree(ENTROPY_EntropySource *es)
{
if (es == NULL) {
return;
}
if (es->isWork == true) {
ENTROPY_EsDeinit(es);
}
BSL_SAL_FREE(es->cfMeth);
ES_NsListFree(es->nsList);
es->nsList = NULL;
BSL_SAL_Free(es);
return;
}
int32_t ENTROPY_EsInit(ENTROPY_EntropySource *es)
{
if (es == NULL || es->cfMeth == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (es->isWork) {
return CRYPT_SUCCESS;
}
ES_CfMethod *meth = es->cfMeth;
if (meth->init != NULL) {
meth->ctx = meth->init(&meth->meth);
if (meth->ctx == NULL) {
ENTROPY_EsDeinit(es);
return CRYPT_ENTROPY_ES_CF_ERROR;
}
}
int32_t ret = ES_NsListInit(es->nsList, es->enableTest);
if (ret != CRYPT_SUCCESS) {
ENTROPY_EsDeinit(es);
return ret;
}
ES_EntropyPool *pool = ES_EntropyPoolInit(es->poolSize);
if (pool == NULL) {
ENTROPY_EsDeinit(es);
return CRYPT_ENTROPY_ES_POOL_ERROR;
}
es->pool = pool;
es->isWork = true;
return CRYPT_SUCCESS;
}
void ENTROPY_EsDeinit(ENTROPY_EntropySource *es)
{
if (es == NULL) {
return;
}
es->isWork = false;
ES_EntropyPoolDeInit(es->pool);
es->pool = NULL;
if (es->cfMeth != NULL && es->cfMeth->deinit != NULL) {
es->cfMeth->deinit(es->cfMeth->ctx);
es->cfMeth->ctx = NULL;
}
ES_NsListDeinit(es->nsList);
return;
}
static int32_t EsPoolSizeSet(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
if (es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
if (len != sizeof(uint32_t) || *(uint32_t *)data < ENTROPY_POOL_SIZE_MIN ||
*(uint32_t *)data > ENTROPY_POOL_SIZE_MAX) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_CTRL_INVALID_PARAM);
return CRYPT_ENTROPY_CTRL_INVALID_PARAM;
}
es->poolSize = *(uint32_t *)data;
return CRYPT_SUCCESS;
}
static int32_t EsNsAdd(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
if (es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
if (data == NULL || len != sizeof(CRYPT_EAL_NsPara)) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
CRYPT_EAL_NsPara *para = (CRYPT_EAL_NsPara *)data;
return ES_NsAdd(es->nsList, para->name, para->autoTest, para->minEntropy, ¶->nsMeth,
(const CRYPT_EAL_NsTestPara *)&(para->nsPara));
}
static int32_t EsEnableTest(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
if (es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
if (data == NULL || len != sizeof(bool)) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
es->enableTest = *(bool *)data;
return CRYPT_SUCCESS;
}
static int32_t EsNsRemove(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
if (es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
if (data == NULL || len == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
return ES_NsRemove(es->nsList, (const char *)data);
}
static int32_t EsSetCF(ENTROPY_EntropySource *es, ENTROPY_CFPara *data)
{
if (es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
if (es->cfMeth != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_CF_ERROR);
return CRYPT_ENTROPY_ES_CF_ERROR;
}
es->cfMeth = ES_CFGetMethod(data->algId, data->md);
if (es->cfMeth == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_CF_NOT_SUPPORT);
return CRYPT_ENTROPY_ES_CF_NOT_SUPPORT;
}
return CRYPT_SUCCESS;
}
static int32_t EsGetSize(ENTROPY_EntropySource *es, int32_t cmd, void *data, uint32_t len)
{
if (data == NULL || len != sizeof(uint32_t)) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (!es->isWork) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_STATE_ERROR);
return CRYPT_ENTROPY_ES_STATE_ERROR;
}
switch (cmd) {
case CRYPT_ENTROPY_GET_POOL_SIZE:
*(uint32_t *)data = es->poolSize;
return CRYPT_SUCCESS;
case CRYPT_ENTROPY_POOL_GET_CURRSIZE:
*(uint32_t *)data = ES_EntropyPoolGetCurSize(es->pool);
return CRYPT_SUCCESS;
case CRYPT_ENTROPY_GET_CF_SIZE:
*(uint32_t *)data = es->cfMeth->getCfOutLen(es->cfMeth->ctx);
return CRYPT_SUCCESS;
default:
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_CTRL_ERROR);
return CRYPT_ENTROPY_ES_CTRL_ERROR;
}
}
static int32_t EsGetState(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
if (data == NULL || len != sizeof(bool)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
*(bool *)data = es->isWork;
return CRYPT_SUCCESS;
}
static int32_t EsSetLogCallback(ENTROPY_EntropySource *es, void *data, uint32_t len)
{
(void)len;
if (es == NULL || data == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
es->runLog = (CRYPT_EAL_EsLogFunc)data;
return CRYPT_SUCCESS;
}
int32_t ENTROPY_EsCtrl(ENTROPY_EntropySource *es, int32_t cmd, void *data, uint32_t len)
{
if (es == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
switch (cmd) {
case CRYPT_ENTROPY_SET_POOL_SIZE:
return EsPoolSizeSet(es, data, len);
case CRYPT_ENTROPY_ADD_NS:
return EsNsAdd(es, data, len);
case CRYPT_ENTROPY_REMOVE_NS:
return EsNsRemove(es, data, len);
case CRYPT_ENTROPY_ENABLE_TEST:
return EsEnableTest(es, data, len);
case CRYPT_ENTROPY_SET_CF:
return EsSetCF(es, data);
case CRYPT_ENTROPY_GET_STATE:
return EsGetState(es, data, len);
case CRYPT_ENTROPY_SET_LOG_CALLBACK:
return EsSetLogCallback(es, data, len);
default:
return EsGetSize(es, cmd, data, len);
}
}
uint32_t ENTROPY_EsEntropyGet(ENTROPY_EntropySource *es, uint8_t *data, uint32_t len)
{
if (es == NULL || !es->isWork || data == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return 0;
}
if (ES_EntropyPoolGetCurSize(es->pool) <= 0) {
int32_t ret = ENTROPY_EsEntropyGather(es);
if (ret != CRYPT_SUCCESS) {
return 0;
}
}
return ES_EntropyPoolPopBytes(es->pool, data, len);
}
static uint32_t EsGetEntropy(ENTROPY_EntropySource *es, uint8_t *buf, uint32_t bufLen, uint32_t entropy)
{
ES_NoiseSource *ns = NULL;
uint32_t needLen = 0;
uint32_t curEntropy = 0;
uint8_t *data = buf;
while (curEntropy < entropy) {
uint32_t tmpEntropy = curEntropy;
for (BslListNode *node = BSL_LIST_FirstNode(es->nsList); node != NULL && needLen < bufLen;
node = BSL_LIST_GetNextNode(es->nsList, node)) {
ns = BSL_LIST_GetData(node);
int32_t ret = ES_NsRead(ns, data, 1);
if (ret == CRYPT_SUCCESS) {
data++;
needLen++;
curEntropy += ns->minEntropy;
}
if (es->runLog != NULL) {
es->runLog(ret);
}
if (curEntropy >= entropy) {
break;
}
}
if (curEntropy == tmpEntropy) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_NS_NOT_AVA);
needLen = 0;
break;
}
}
return needLen;
}
static uint32_t GetMinLen(uint32_t entropy, uint32_t minEntropy)
{
return (uint32_t)(((uint64_t)entropy + (uint64_t)minEntropy - 1) / (uint64_t)minEntropy);
}
int32_t ENTROPY_EsEntropyGather(ENTROPY_EntropySource *es)
{
if (es == NULL || es->isWork == false || es->cfMeth == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
ES_CfMethod *meth = es->cfMeth;
if ((meth->getCfOutLen(meth->ctx) > (uint32_t)ES_EntropyPoolGetMaxSize(es->pool) -
ES_EntropyPoolGetCurSize(es->pool))) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_POOL_INSUFFICIENT);
return CRYPT_ENTROPY_ES_POOL_INSUFFICIENT;
}
uint32_t minEntropy = ES_NsListGetMinEntropy(es->nsList);
uint32_t needEntropy = meth->getNeedEntropy(meth->ctx);
uint32_t bufLen = GetMinLen(needEntropy, minEntropy);
uint8_t *buf = BSL_SAL_Malloc(bufLen);
if (buf == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
uint32_t needLen = EsGetEntropy(es, buf, bufLen, needEntropy);
if (needLen == 0) {
BSL_SAL_ClearFree(buf, bufLen);
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_ENTROPY_NOT_ENOUGH);
return CRYPT_ENTROPY_ES_ENTROPY_NOT_ENOUGH;
}
int32_t ret = meth->update(meth->ctx, buf, needLen);
BSL_SAL_ClearFree(buf, bufLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
uint32_t len;
uint8_t *data = meth->getEntropyData(meth->ctx, &len);
if (data == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ret = ES_EntropyPoolPushBytes(es->pool, data, len);
BSL_SAL_ClearFree(data, len);
return ret;
}
#endif