* 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 "crypt_errno.h"
#include "bsl_list.h"
#include "bsl_err_internal.h"
#include "es_noise_source.h"
#define ES_NS_MAX_SIZE 16
#define ES_MIN_ENTROPY_MAX 8
#define ES_MAX_TIMEOUT_MAX 10
static int32_t NsRead(ES_NoiseSource *ns, uint8_t *buf, uint32_t bufLen);
* GM/T 0105-2021 Section 5.5
* The Power-Up Health Test requires a continuous health test of at least 1024 consecutive samples.
*/
#define ENTROPY_START_UP_TEST_SIZE 1024
int32_t ES_NoiseSourceStartupTest(ES_NoiseSource *ns)
{
uint8_t buf[ENTROPY_START_UP_TEST_SIZE] = {0};
return NsRead(ns, buf, ENTROPY_START_UP_TEST_SIZE);
}
static ES_NoiseSource *ES_NsCreate(const char *name, bool autoTest, uint32_t minEntropy,
const CRYPT_EAL_NsMethod *method, const CRYPT_EAL_NsTestPara *para)
{
ES_NoiseSource *ctx = BSL_SAL_Malloc(sizeof(ES_NoiseSource));
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
memset(ctx, 0, sizeof(ES_NoiseSource));
uint32_t len = strlen(name) + 1;
ctx->name = BSL_SAL_Malloc(len);
if (ctx->name == NULL) {
BSL_SAL_FREE(ctx);
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
strcpy(ctx->name, name);
ctx->autoTest = autoTest;
ctx->para = method->para;
ctx->init = method->init;
ctx->read = method->read;
ctx->deinit = method->deinit;
ctx->minEntropy = minEntropy;
ctx->state.rctCutoff = para->rctCutoff;
ctx->state.aptCutOff = para->aptCutoff;
ctx->state.aptWindowSize = para->aptWinSize;
return ctx;
}
static void ES_NsFree(ES_NoiseSource *ns)
{
if (ns->usrdata != NULL && ns->deinit != NULL) {
ns->deinit(ns->usrdata);
}
BSL_SAL_FREE(ns->name);
BSL_SAL_Free(ns);
return;
}
BslList *ES_NsListCreat(void)
{
BslList *ns = BSL_LIST_New(sizeof(BslListNode));
if (ns == NULL) {
BSL_ERR_PUSH_ERROR(BSL_LIST_MALLOC_FAIL);
return NULL;
}
int32_t ret;
#ifndef HITLS_BSL_SAL_DARWIN
* 1. Disabled on macOS because health test thresholds (RCT/APT) need adjustment
* 2. Jitter is platform-independent in theory but requires per-platform validation
*/
ES_NoiseSource *jitterCtx = ES_CpuJitterGetCtx();
if (jitterCtx == NULL) {
goto ERR;
}
ret = BSL_LIST_AddElement(ns, jitterCtx, BSL_LIST_POS_END);
if (ret != CRYPT_SUCCESS) {
ES_NsFree(jitterCtx);
goto ERR;
}
#endif
ES_NoiseSource *stampCtx = ES_TimeStampGetCtx();
if (stampCtx == NULL) {
goto ERR;
}
ret = BSL_LIST_AddElement(ns, stampCtx, BSL_LIST_POS_END);
if (ret != CRYPT_SUCCESS) {
ES_NsFree(stampCtx);
goto ERR;
}
return ns;
ERR:
BSL_LIST_FREE(ns, (BSL_LIST_PFUNC_FREE)ES_NsFree);
return NULL;
}
int32_t ES_NsListInit(BslList *nsList, bool enableTest)
{
if (BSL_LIST_COUNT(nsList) == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_NO_NS);
return CRYPT_ENTROPY_ES_NO_NS;
}
bool nsUsed = false;
BSL_ERR_SET_MARK();
for (BslListNode *node = BSL_LIST_FirstNode(nsList); node != NULL; node = BSL_LIST_GetNextNode(nsList, node)) {
ES_NoiseSource *ns = BSL_LIST_GetData(node);
* If the health check is automatically performed when the noise source is generated, no additional health
* check is required. Otherwise, determine whether to perform the health check based on the configuration.
*/
ns->enableTest = (ns->autoTest) ? false : enableTest;
if (ns->init != NULL) {
ns->usrdata = ns->init(ns->para);
if (ns->usrdata == NULL) {
ns->isEnable = false;
continue;
}
}
ns->isInit = true;
if (enableTest) {
int32_t ret = ES_NoiseSourceStartupTest(ns);
if (ret != CRYPT_SUCCESS) {
ns->isEnable = false;
BSL_ERR_PUSH_ERROR(ret);
continue;
}
}
ns->isEnable = true;
nsUsed = true;
}
if (!nsUsed) {
ES_NsListDeinit(nsList);
return CRYPT_ENTROPY_ES_NO_NS;
}
BSL_ERR_POP_TO_MARK();
return CRYPT_SUCCESS;
}
void ES_NsListDeinit(BslList *nsList)
{
if (BSL_LIST_COUNT(nsList) == 0) {
return;
}
for (BslListNode *node = BSL_LIST_FirstNode(nsList); node != NULL; node = BSL_LIST_GetNextNode(nsList, node)) {
ES_NoiseSource *ns = BSL_LIST_GetData(node);
ns->isInit = false;
ns->isEnable = false;
if (ns->deinit != NULL) {
ns->deinit(ns->usrdata);
ns->usrdata = NULL;
}
}
return;
}
void ES_NsListFree(BslList *nsList)
{
BSL_LIST_FREE(nsList, (BSL_LIST_PFUNC_FREE)ES_NsFree);
}
static int32_t ES_NsComp(const ES_NoiseSource *ns, const char *name)
{
return strcmp(ns->name, name);
}
int32_t ES_NsAdd(BslList *nsList, const char *name, bool autoTest, uint32_t minEntropy,
const CRYPT_EAL_NsMethod *method, const CRYPT_EAL_NsTestPara *para)
{
if (name == NULL || minEntropy > ES_MIN_ENTROPY_MAX) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (method->read == NULL || (method->init == NULL && method->deinit != NULL) ||
(method->init != NULL && method->deinit == NULL)) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (BSL_LIST_COUNT(nsList) >= ES_NS_MAX_SIZE) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_NS_FULL);
return CRYPT_ENTROPY_ES_NS_FULL;
}
if (BSL_LIST_SearchDataConst(nsList, name, (BSL_LIST_PFUNC_CMP)ES_NsComp, NULL) != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_DUP_NS);
return CRYPT_ENTROPY_ES_DUP_NS;
}
ES_NoiseSource *ns = ES_NsCreate(name, autoTest, minEntropy, method, para);
if (ns == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_CREATE_ERROR);
return CRYPT_ENTROPY_ES_CREATE_ERROR;
}
int32_t ret = BSL_LIST_AddElement(nsList, ns, BSL_LIST_POS_END);
if (ret != CRYPT_SUCCESS) {
ES_NsFree(ns);
}
return ret;
}
int32_t ES_NsRemove(BslList *nsList, const char *name)
{
BslListNode *tmpNode = NULL;
for (BslListNode *node = BSL_LIST_FirstNode(nsList); node != NULL;) {
tmpNode = node;
ES_NoiseSource *ns = BSL_LIST_GetData(tmpNode);
if (ns == NULL) {
node = BSL_LIST_GetNextNode(nsList, tmpNode);
continue;
}
if (strcmp(ns->name, name) == 0) {
BSL_LIST_DeleteNode(nsList, (const BslListNode *)tmpNode, (BSL_LIST_PFUNC_FREE)ES_NsFree);
return CRYPT_SUCCESS;
}
node = BSL_LIST_GetNextNode(nsList, tmpNode);
}
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_NS_NOT_FOUND);
return CRYPT_ENTROPY_ES_NS_NOT_FOUND;
}
static int32_t NsRead(ES_NoiseSource *ns, uint8_t *buf, uint32_t bufLen)
{
int32_t ret = ns->read(ns->usrdata, ES_MAX_TIMEOUT_MAX, buf, bufLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
if (!ns->enableTest) {
return CRYPT_SUCCESS;
}
for (uint32_t iter = 0; iter < bufLen; iter++) {
ret = ES_HealthTestRct(&(ns->state), buf[iter]);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = ES_HealthTestApt(&(ns->state), buf[iter]);
if (ret != CRYPT_SUCCESS) {
return ret;
}
}
return ret;
}
int32_t ES_NsRead(ES_NoiseSource *ns, uint8_t *buf, uint32_t bufLen)
{
if (ns->isInit != true || ns->isEnable != true) {
return CRYPT_ENTROPY_ES_NS_NOT_AVA;
}
return NsRead(ns, buf, bufLen);
}
uint32_t ES_NsListGetMinEntropy(BslList *nsList)
{
if (BSL_LIST_COUNT(nsList) == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_ENTROPY_ES_NO_NS);
return 0;
}
uint32_t minEntropy = 8;
for (BslListNode *node = BSL_LIST_FirstNode(nsList); node != NULL; node = BSL_LIST_GetNextNode(nsList, node)) {
ES_NoiseSource *ns = BSL_LIST_GetData(node);
minEntropy = (ns->minEntropy < minEntropy) ? ns->minEntropy : minEntropy;
}
return minEntropy;
}
#endif