blob: d9e1274b906d2a39ba124023ba9e04e8f5f86521 [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/common/espressif/esp_sha.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_ESPRESSIF_SHA_ACCELERATOR
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <debug.h>
#include <nuttx/mutex.h>
#include "esp_sha.h"
#include "esp_private/periph_ctrl.h"
#include "soc/periph_defs.h"
#include "hal/sha_hal.h"
#include "soc/soc_caps.h"
#include "rom/cache.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG
# error "Parallel engine feature is not supported."
#endif
#define PUT_UINT32_BE(n,b,i) \
{ \
(b)[(i)] = (unsigned char) ((n) >> 24); \
(b)[(i) + 1] = (unsigned char) ((n) >> 16); \
(b)[(i) + 2] = (unsigned char) ((n) >> 8); \
(b)[(i) + 3] = (unsigned char) ((n)); \
}
#define PUT_UINT64_BE(n,b,i) \
{ \
(b)[(i) ] = (unsigned char) ( (n) >> 56 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 48 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 40 ); \
(b)[(i) + 3] = (unsigned char) ( (n) >> 32 ); \
(b)[(i) + 4] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 5] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 6] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 7] = (unsigned char) ( (n) ); \
}
#define SHA1_BLK_SIZE (20)
#define SHA2_BLK_SIZE (32)
#define SHA3_BLK_SIZE (64)
/****************************************************************************
* Private Data
****************************************************************************/
static bool g_sha_inited;
static mutex_t g_sha_lock = NXMUTEX_INITIALIZER;
static const unsigned char esp_sha_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const unsigned char sha512_padding[128] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: esp_sha_hash_block
*
* Description:
* Performs SHA on multiple blocks at a time.
*
* Input Parameters:
* type - Type of SHA
* first_block - Pointer of the ctx struct about is block first
* state - Pointer of the ctx struct about state
* data - Input message to be hashed on single block
* len - Length of the input message on single block
* buf - Input message to be hashed on multiple blocks
* buf_len - Length of the input message on multiple blocks
*
* Returned Value:
* OK is returned on success.
*
****************************************************************************/
static int esp_sha_hash_block(enum esp_sha_type_e type,
bool *first_block, void *state,
const uint8_t *data, size_t len,
uint8_t *buf, size_t buf_len)
{
uint32_t *data_words = NULL;
size_t blk_len = 0;
size_t blk_word_len = 0;
int num_block = 0;
int i;
int j;
if (type < ESP_SHA3_384)
{
blk_len = 64;
}
else
{
blk_len = 128;
}
blk_word_len = blk_len / 4;
num_block = len / blk_len;
if (buf_len != 0)
{
sha_hal_hash_block(type, buf, blk_word_len, (*first_block));
(*first_block) = false;
}
for (j = 0; j < num_block; j++)
{
data_words = (uint32_t *)(data + blk_len * j);
sha_hal_hash_block(type, data_words, blk_word_len, (*first_block));
(*first_block) = false;
}
sha_hal_read_digest(type, state);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp_sha1_init
*
* Description:
* Initializes a SHA-1 context.
*
* Input Parameters:
* ctx - The SHA-1 context to initialize
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha1_init(struct esp_sha1_context_s *ctx)
{
memset(ctx, 0, sizeof(struct esp_sha1_context_s));
}
/****************************************************************************
* Name: esp_sha1_starts
*
* Description:
* Starts a SHA-1 checksum calculation.
*
* Input Parameters:
* ctx - The SHA-1 context to initialize
*
* Returned Value:
* OK is returned on success.
*
****************************************************************************/
int esp_sha1_starts(struct esp_sha1_context_s *ctx)
{
memset(ctx, 0, sizeof(struct esp_sha1_context_s));
ctx->mode = ESP_SHA1_1;
return OK;
}
/****************************************************************************
* Name: esp_sha1_update
*
* Description:
* Feeds an input buffer into an ongoing SHA-1 checksum calculation.
*
* Input Parameters:
* ctx - The SHA-1 context to use
* input - The buffer holding the input data
* ilen - The length of the input data in Bytes
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha1_update(struct esp_sha1_context_s *ctx,
const unsigned char *input,
size_t ilen)
{
int ret;
size_t fill;
uint32_t left;
uint32_t len;
uint32_t local_len = 0;
int i;
if (!ilen || (input == NULL))
{
return OK;
}
left = ctx->total[0] & 0x3f;
fill = 64 - left;
ctx->total[0] += ilen;
ctx->total[0] &= UINT32_MAX;
if (ctx->total[0] < ilen)
{
ctx->total[1]++;
}
if (left && ilen >= fill)
{
memcpy((void *) (ctx->buffer + left), input, fill);
input += fill;
ilen -= fill;
left = 0;
local_len = 64;
}
len = (ilen / 64) * 64;
if (len || local_len)
{
ret = nxmutex_lock(&g_sha_lock);
if (ret < 0)
{
return ret;
}
if (ctx->sha_state == ESP_SHA_STATE_INIT)
{
ctx->first_block = true;
ctx->sha_state = ESP_SHA_STATE_IN_PROCESS;
}
else if (ctx->sha_state == ESP_SHA_STATE_IN_PROCESS)
{
ctx->first_block = false;
sha_hal_write_digest(ctx->mode, ctx->state);
}
ret = esp_sha_hash_block(ctx->mode, &ctx->first_block, ctx->state,
input, len, ctx->buffer, local_len);
ret |= nxmutex_unlock(&g_sha_lock);
if (ret != 0)
{
return ret;
}
}
if (ilen > 0)
{
memcpy((void *) (ctx->buffer + left), input + len, ilen - len);
}
return OK;
}
/****************************************************************************
* Name: esp_sha1_finish
*
* Description:
* Finishes the SHA-1 operation,
* and writes the result to the output buffer.
*
* Input Parameters:
* ctx - The SHA-1 context to use
* output - The SHA-1 checksum result
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha1_finish(struct esp_sha1_context_s *ctx,
unsigned char output[20])
{
int ret;
uint32_t last;
uint32_t padn;
uint32_t high;
uint32_t low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3f;
padn = (last < 56) ? (56 - last) : (120 - last);
ret = esp_sha1_update(ctx, esp_sha_padding, padn);
if (ret != 0)
{
return ret;
}
ret = esp_sha1_update(ctx, msglen, 8);
if (ret != 0)
{
return ret;
}
memcpy(output, ctx->state, SHA1_BLK_SIZE);
return ret;
}
/****************************************************************************
* Name: esp_sha1_free
*
* Description:
* Clears a SHA-1 context.
*
* Input Parameters:
* ctx - The SHA-1 context to clear
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha1_free(struct esp_sha1_context_s *ctx)
{
if (ctx == NULL)
{
return;
}
memset(ctx, 0, sizeof(struct esp_sha1_context_s));
}
/****************************************************************************
* Name: esp_sha256_init
*
* Description:
* Initializes a SHA-256 context.
*
* Input Parameters:
* ctx - The SHA-256 context to initialize
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha256_init(struct esp_sha256_context_s *ctx)
{
memset(ctx, 0, sizeof(struct esp_sha256_context_s));
}
/****************************************************************************
* Name: esp_sha256_starts
*
* Description:
* Starts a SHA-224 or SHA-256 checksum calculation.
*
* Input Parameters:
* ctx - The SHA-256 context to initialize
* is224 - Determines which function to use
*
* Returned Value:
* OK is returned on success.
*
****************************************************************************/
int esp_sha256_starts(struct esp_sha256_context_s *ctx, bool is224)
{
memset(ctx, 0, sizeof(struct esp_sha256_context_s));
if (is224)
{
ctx->mode = ESP_SHA2_224;
}
else
{
ctx->mode = ESP_SHA2_256;
}
return OK;
}
/****************************************************************************
* Name: esp_sha256_update
*
* Description:
* Feeds an input buffer into an ongoing SHA-224 or SHA-256
* checksum calculation.
*
* Input Parameters:
* ctx - The SHA-256 context to use
* input - The buffer holding the input data
* ilen - The length of the input data in Bytes
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha256_update(struct esp_sha256_context_s *ctx,
const unsigned char *input,
size_t ilen)
{
int ret = 0;
size_t fill;
uint32_t left;
uint32_t len;
uint32_t local_len = 0;
int i;
if (ilen == 0)
{
return OK;
}
left = ctx->total[0] & 0x3f;
fill = 64 - left;
ctx->total[0] += ilen;
ctx->total[0] &= UINT32_MAX;
if (ctx->total[0] < ilen)
{
ctx->total[1]++;
}
/* Check if any data pending from previous call to this API */
if (left && ilen >= fill)
{
memcpy((void *) (ctx->buffer + left), input, fill);
input += fill;
ilen -= fill;
left = 0;
local_len = 64;
}
len = (ilen / 64) * 64;
if (len || local_len)
{
ret = nxmutex_lock(&g_sha_lock);
if (ret < 0)
{
return ret;
}
if (ctx->sha_state == ESP_SHA_STATE_INIT)
{
ctx->first_block = true;
ctx->sha_state = ESP_SHA_STATE_IN_PROCESS;
}
else if (ctx->sha_state == ESP_SHA_STATE_IN_PROCESS)
{
ctx->first_block = false;
sha_hal_write_digest(ctx->mode, ctx->state);
}
ret = esp_sha_hash_block(ctx->mode, &ctx->first_block, ctx->state,
input, len, ctx->buffer, local_len);
ret |= nxmutex_unlock(&g_sha_lock);
if (ret != 0)
{
return ret;
}
}
if (ilen > 0)
{
memcpy((void *) (ctx->buffer + left), input + len, ilen - len);
}
return OK;
}
/****************************************************************************
* Name: esp_sha256_finish
*
* Description:
* Finishes the SHA-224 or SHA-256 operation, and writes the result to
* the output buffer.
*
* Input Parameters:
* ctx - The SHA-256 context to use
* output - The SHA-256 checksum result
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha256_finish(struct esp_sha256_context_s *ctx,
unsigned char output[32])
{
int ret;
uint32_t last;
uint32_t padn;
uint32_t high;
uint64_t low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3f;
padn = (last < 56) ? (56 - last) : (120 - last);
ret = esp_sha256_update(ctx, esp_sha_padding, padn);
if (ret != 0)
{
return ret;
}
ret = esp_sha256_update(ctx, msglen, 8);
if (ret != 0)
{
return ret;
}
memcpy(output, ctx->state, SHA2_BLK_SIZE);
return ret;
}
/****************************************************************************
* Name: esp_sha256_free
*
* Description:
* Clears a SHA-256 context.
*
* Input Parameters:
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha256_free(struct esp_sha256_context_s *ctx)
{
if (ctx == NULL)
{
return;
}
memset(ctx, 0, sizeof(struct esp_sha256_context_s));
}
/****************************************************************************
* Name: esp_sha512_init
*
* Description:
* Initializes a SHA-512 context.
*
* Input Parameters:
* ctx - The SHA-512 context to initialize
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha512_init(struct esp_sha512_context_s *ctx)
{
memset(ctx, 0, sizeof(struct esp_sha512_context_s));
}
/****************************************************************************
* Name: esp_sha512_starts
*
* Description:
* Starts a SHA-384 or SHA-512 checksum calculation.
*
* Input Parameters:
* ctx - The SHA-512 context to initialize
* is384 - Determines which function to use
*
* Returned Value:
* OK is returned on success.
*
****************************************************************************/
int esp_sha512_starts(struct esp_sha512_context_s *ctx, bool is384)
{
memset(ctx, 0, sizeof(struct esp_sha512_context_s));
if (is384)
{
ctx->mode = ESP_SHA3_384;
}
else
{
ctx->mode = ESP_SHA3_512;
}
return OK;
}
/****************************************************************************
* Name: esp_sha512_update
*
* Description:
* Feeds an input buffer into an ongoing SHA-384 or SHA-512
* checksum calculation.
*
* Input Parameters:
* ctx - The SHA-512 context to use
* input - The buffer holding the input data
* ilen - The length of the input data in Bytes
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha512_update(struct esp_sha512_context_s *ctx,
const unsigned char *input,
size_t ilen)
{
int ret = 0;
size_t fill;
uint32_t left;
uint32_t len;
uint32_t local_len = 0;
int i;
if (ilen == 0)
{
return OK;
}
left = ctx->total[0] & 0x7f;
fill = 128 - left;
ctx->total[0] += ilen;
if (ctx->total[0] < ilen)
{
ctx->total[1]++;
}
/* Check if any data pending from previous call to this API */
if (left && ilen >= fill)
{
memcpy((void *) (ctx->buffer + left), input, fill);
input += fill;
ilen -= fill;
left = 0;
local_len = 128;
}
len = (ilen / 128) * 128;
if (len || local_len)
{
ret = nxmutex_lock(&g_sha_lock);
if (ret < 0)
{
return ret;
}
if (ctx->sha_state == ESP_SHA_STATE_INIT)
{
ctx->first_block = true;
ctx->sha_state = ESP_SHA_STATE_IN_PROCESS;
}
else if (ctx->sha_state == ESP_SHA_STATE_IN_PROCESS)
{
ctx->first_block = false;
sha_hal_write_digest(ctx->mode, ctx->state);
}
ret = esp_sha_hash_block(ctx->mode, &ctx->first_block, ctx->state,
input, len, ctx->buffer, local_len);
ret |= nxmutex_unlock(&g_sha_lock);
if (ret != 0)
{
return ret;
}
}
if (ilen > 0)
{
memcpy((void *) (ctx->buffer + left), input + len, ilen - len);
}
return OK;
}
/****************************************************************************
* Name: esp_sha512_finish
*
* Description:
* Finishes the SHA-384 or SHA-512 operation, and writes the result to
* the output buffer.
*
* Input Parameters:
* ctx - The SHA-512 context to use
* output - The SHA-512 checksum result
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha512_finish(struct esp_sha512_context_s *ctx,
unsigned char output[64])
{
int ret;
uint32_t last;
uint32_t padn;
uint64_t high;
uint64_t low;
unsigned char msglen[16];
high = (ctx->total[0] >> 61) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT64_BE(high, msglen, 0);
PUT_UINT64_BE(low, msglen, 8);
last = ctx->total[0] & 0x7f;
padn = (last < 112) ? (112 - last) : (240 - last);
ret = esp_sha512_update(ctx, sha512_padding, padn);
if (ret != 0)
{
return ret;
}
ret = esp_sha512_update(ctx, msglen, 16);
if (ret != 0)
{
return ret;
}
if (ctx->mode == ESP_SHA3_384)
{
memcpy(output, ctx->state, 48);
}
else
{
memcpy(output, ctx->state, 64);
}
return ret;
}
/****************************************************************************
* Name: esp_sha512_free
*
* Description:
* Clears a SHA-512 context.
*
* Input Parameters:
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_sha512_free(struct esp_sha512_context_s *ctx)
{
if (ctx == NULL)
{
return;
}
memset(ctx, 0, sizeof(struct esp_sha512_context_s));
}
/****************************************************************************
* Name: esp_sha_init
*
* Description:
* Initialize ESP device SHA hardware.
*
* Input Parameters:
* None
*
* Returned Value:
* OK is returned on success.
* Otherwise, a negated errno value is returned.
*
****************************************************************************/
int esp_sha_init(void)
{
if (!g_sha_inited)
{
periph_module_enable(PERIPH_SHA_MODULE);
g_sha_inited = true;
}
else
{
return -EBUSY;
}
return OK;
}
#endif