mbedTLS SHA acceleration: Allow concurrent digest calculation, works with TLS

SHA hardware allows each of SHA1, SHA256, SHA384&SHA512 to calculate digests
concurrently.

Currently incompatible with AES acceleration due to a hardware reset problem.

Ref TW7111.
This commit is contained in:
Angus Gratton
2016-11-20 16:29:29 +11:00
parent 4261fc5ef7
commit c48612e516
17 changed files with 1862 additions and 498 deletions

View File

@@ -26,242 +26,241 @@
*/
#include <string.h>
#include <stdio.h>
#include <sys/lock.h>
#include <byteswap.h>
#include <assert.h>
#include "hwcrypto/sha.h"
#include "rom/ets_sys.h"
#include "soc/dport_reg.h"
#include "soc/hwcrypto_reg.h"
static _lock_t sha_lock;
void esp_sha_acquire_hardware( void )
{
/* newlib locks lazy initialize on ESP-IDF */
_lock_acquire(&sha_lock);
ets_sha_enable();
inline static uint32_t SHA_LOAD_REG(esp_sha_type sha_type) {
return SHA_1_LOAD_REG + sha_type * 0x10;
}
void esp_sha_release_hardware( void )
{
/* Want to empty internal SHA buffers where possible,
need to check if this is sufficient for this. */
SHA_CTX zero = { 0 };
ets_sha_init(&zero);
ets_sha_disable();
_lock_release(&sha_lock);
inline static uint32_t SHA_BUSY_REG(esp_sha_type sha_type) {
return SHA_1_BUSY_REG + sha_type * 0x10;
}
/* Generic esp_shaX_update implementation */
static void esp_sha_update( esp_sha_context *ctx, const unsigned char *input, size_t ilen, size_t block_size)
inline static uint32_t SHA_START_REG(esp_sha_type sha_type) {
return SHA_1_START_REG + sha_type * 0x10;
}
inline static uint32_t SHA_CONTINUE_REG(esp_sha_type sha_type) {
return SHA_1_CONTINUE_REG + sha_type * 0x10;
}
/* Single lock for SHA engine memory block
*/
static _lock_t memory_block_lock;
typedef struct {
_lock_t lock;
bool in_use;
} sha_engine_state;
/* Pointer to state of each concurrent SHA engine.
Indexes:
0 = SHA1
1 = SHA2_256
2 = SHA2_384 or SHA2_512
*/
static sha_engine_state engine_states[3];
/* Index into the sha_engine_state array */
inline static size_t sha_engine_index(esp_sha_type type) {
switch(type) {
case SHA1:
return 0;
case SHA2_256:
return 1;
default:
return 2;
}
}
/* Return state & digest length (in bytes) for a given SHA type */
inline static size_t sha_length(esp_sha_type type) {
switch(type) {
case SHA1:
return 20;
case SHA2_256:
return 32;
case SHA2_384:
return 64;
case SHA2_512:
return 64;
default:
return 0;
}
}
/* Return block size (in bytes) for a given SHA type */
inline static size_t block_length(esp_sha_type type) {
switch(type) {
case SHA1:
case SHA2_256:
return 64;
case SHA2_384:
case SHA2_512:
return 128;
default:
return 0;
}
}
void esp_sha_lock_memory_block(void)
{
/* Feed the SHA engine one block at a time */
_lock_acquire(&memory_block_lock);
}
void esp_sha_unlock_memory_block(void)
{
_lock_release(&memory_block_lock);
}
/* Lock to hold when changing SHA engine state,
allows checking of sha_engines_all_idle()
*/
static _lock_t state_change_lock;
inline static bool sha_engines_all_idle() {
return !engine_states[0].in_use
&& !engine_states[1].in_use
&& !engine_states[2].in_use;
}
bool esp_sha_try_lock_engine(esp_sha_type sha_type)
{
sha_engine_state *engine = &engine_states[sha_engine_index(sha_type)];
if(_lock_try_acquire(&engine->lock) != 0) {
/* This SHA engine is already in use */
return false;
}
_lock_acquire(&state_change_lock);
assert( !engine->in_use && "in_use flag should be cleared" );
if (sha_engines_all_idle()) {
ets_sha_enable();
}
_lock_release(&state_change_lock);
engine->in_use = true;
return true;
}
void esp_sha_unlock_engine(esp_sha_type sha_type)
{
sha_engine_state *engine = &engine_states[sha_engine_index(sha_type)];
_lock_acquire(&state_change_lock);
assert( engine->in_use && "in_use flag should be set" );
engine->in_use = false;
if (sha_engines_all_idle()) {
ets_sha_disable();
}
_lock_release(&state_change_lock);
_lock_release(&engine->lock);
}
void esp_sha_wait_idle(void)
{
while(REG_READ(SHA_1_BUSY_REG) == 1) {}
while(REG_READ(SHA_256_BUSY_REG) == 1) {}
while(REG_READ(SHA_384_BUSY_REG) == 1) {}
while(REG_READ(SHA_512_BUSY_REG) == 1) {}
}
void esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state)
{
sha_engine_state *engine = &engine_states[sha_engine_index(sha_type)];
assert(engine->in_use && "SHA engine should be locked" );
esp_sha_lock_memory_block();
esp_sha_wait_idle();
REG_WRITE(SHA_LOAD_REG(sha_type), 1);
while(REG_READ(SHA_BUSY_REG(sha_type)) == 1) { }
uint32_t *digest_state_words = (uint32_t *)digest_state;
uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE);
if(sha_type == SHA2_384 || sha_type == SHA2_512) {
/* for these ciphers using 64-bit states, swap each pair of words */
for(int i = 0; i < sha_length(sha_type)/4; i += 2) {
digest_state_words[i+1] = reg_addr_buf[i];
digest_state_words[i]= reg_addr_buf[i+1];
}
} else {
memcpy(digest_state_words, reg_addr_buf, sha_length(sha_type));
}
asm volatile ("memw");
esp_sha_unlock_memory_block();
}
void esp_sha_block(esp_sha_type sha_type, const void *data_block, bool is_first_block)
{
sha_engine_state *engine = &engine_states[sha_engine_index(sha_type)];
assert(engine->in_use && "SHA engine should be locked" );
esp_sha_lock_memory_block();
esp_sha_wait_idle();
/* Fill the data block */
uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE);
uint32_t *data_words = (uint32_t *)data_block;
for (int i = 0; i < block_length(sha_type) / 4; i++) {
reg_addr_buf[i] = __bswap_32(data_words[i]);
}
asm volatile ("memw");
if(is_first_block) {
REG_WRITE(SHA_START_REG(sha_type), 1);
} else {
REG_WRITE(SHA_CONTINUE_REG(sha_type), 1);
}
esp_sha_unlock_memory_block();
/* Note: deliberately not waiting for this operation to complete,
as a performance tweak - delay waiting until the next time we need the SHA
unit, instead.
*/
}
void esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, unsigned char *output)
{
size_t block_len = block_length(sha_type);
esp_sha_try_lock_engine(sha_type);
SHA_CTX ctx;
ets_sha_init(&ctx);
while(ilen > 0) {
size_t chunk_len = (ilen > block_size) ? block_size : ilen;
ets_sha_update(&ctx->context, ctx->context_type, input, chunk_len * 8);
size_t chunk_len = (ilen > block_len) ? block_len : ilen;
esp_sha_lock_memory_block();
esp_sha_wait_idle();
ets_sha_update(&ctx, sha_type, input, chunk_len * 8);
esp_sha_unlock_memory_block();
input += chunk_len;
ilen -= chunk_len;
}
esp_sha_lock_memory_block();
esp_sha_wait_idle();
ets_sha_finish(&ctx, sha_type, output);
esp_sha_unlock_memory_block();
esp_sha_unlock_engine(sha_type);
}
void esp_sha1_init( esp_sha_context *ctx )
{
bzero( ctx, sizeof( esp_sha_context ) );
}
void esp_sha1_free( esp_sha_context *ctx )
{
if ( ctx == NULL ) {
return;
}
bzero( ctx, sizeof( esp_sha_context ) );
}
void esp_sha1_clone( esp_sha_context *dst, const esp_sha_context *src )
{
*dst = *src;
}
/*
* SHA-1 context setup
*/
void esp_sha1_start( esp_sha_context *ctx )
{
ctx->context_type = SHA1;
esp_sha_acquire_hardware();
ets_sha_init(&ctx->context);
}
/*
* SHA-1 process buffer
*/
void esp_sha1_update( esp_sha_context *ctx, const unsigned char *input, size_t ilen )
{
esp_sha_update(ctx, input, ilen, 64);
}
/*
* SHA-1 final digest
*/
void esp_sha1_finish( esp_sha_context *ctx, unsigned char output[20] )
{
ets_sha_finish(&ctx->context, ctx->context_type, output);
esp_sha_release_hardware();
}
/* Full SHA-1 calculation */
void esp_sha1( const unsigned char *input, size_t ilen, unsigned char output[20] )
{
esp_sha_context ctx;
esp_sha1_init( &ctx );
esp_sha1_start( &ctx );
esp_sha1_update( &ctx, input, ilen );
esp_sha1_finish( &ctx, output );
esp_sha1_free( &ctx );
}
void esp_sha256_init( esp_sha_context *ctx )
{
bzero( ctx, sizeof( esp_sha_context ) );
}
void esp_sha256_free( esp_sha_context *ctx )
{
if ( ctx == NULL ) {
return;
}
bzero( ctx, sizeof( esp_sha_context ) );
}
void esp_sha256_clone( esp_sha_context *dst, const esp_sha_context *src )
{
*dst = *src;
}
/*
* SHA-256 context setup
*/
void esp_sha256_start( esp_sha_context *ctx, int is224 )
{
if ( is224 == 0 ) {
/* SHA-256 */
ctx->context_type = SHA2_256;
esp_sha_acquire_hardware();
ets_sha_init(&ctx->context);
} else {
/* SHA-224 is not supported! */
ctx->context_type = SHA_INVALID;
}
}
/*
* SHA-256 process buffer
*/
void esp_sha256_update( esp_sha_context *ctx, const unsigned char *input, size_t ilen )
{
if( ctx->context_type == SHA2_256 ) {
esp_sha_update(ctx, input, ilen, 64);
}
/* SHA-224 is a no-op */
}
/*
* SHA-256 final digest
*/
void esp_sha256_finish( esp_sha_context *ctx, unsigned char output[32] )
{
if ( ctx->context_type == SHA2_256 ) {
ets_sha_finish(&ctx->context, ctx->context_type, output);
esp_sha_release_hardware();
} else {
/* No hardware SHA-224 support, but mbedTLS API doesn't allow failure.
For now, zero the output to make it clear it's not valid. */
bzero( output, 28 );
}
}
/*
* Full SHA-256 calculation
*/
void esp_sha256( const unsigned char *input, size_t ilen, unsigned char output[32], int is224 )
{
esp_sha_context ctx;
esp_sha256_init( &ctx );
esp_sha256_start( &ctx, is224 );
esp_sha256_update( &ctx, input, ilen );
esp_sha256_finish( &ctx, output );
esp_sha256_free( &ctx );
}
/////
void esp_sha512_init( esp_sha_context *ctx )
{
memset( ctx, 0, sizeof( esp_sha_context ) );
}
void esp_sha512_free( esp_sha_context *ctx )
{
if ( ctx == NULL ) {
return;
}
bzero( ctx, sizeof( esp_sha_context ) );
}
void esp_sha512_clone( esp_sha_context *dst, const esp_sha_context *src )
{
*dst = *src;
}
/*
* SHA-512 context setup
*/
void esp_sha512_start( esp_sha_context *ctx, int is384 )
{
if ( is384 == 0 ) {
/* SHA-512 */
ctx->context_type = SHA2_512;
} else {
/* SHA-384 */
ctx->context_type = SHA2_384;
}
esp_sha_acquire_hardware();
ets_sha_init(&ctx->context);
}
/*
* SHA-512 process buffer
*/
void esp_sha512_update( esp_sha_context *ctx, const unsigned char *input, size_t ilen )
{
esp_sha_update(ctx, input, ilen, 128);
}
/*
* SHA-512 final digest
*/
void esp_sha512_finish( esp_sha_context *ctx, unsigned char output[64] )
{
ets_sha_finish(&ctx->context, ctx->context_type, output);
esp_sha_release_hardware();
}
/*
* Full SHA-512 calculation
*/
void esp_sha512( const unsigned char *input, size_t ilen, unsigned char output[64], int is384 )
{
esp_sha_context ctx;
esp_sha512_init( &ctx );
esp_sha512_start( &ctx, is384 );
esp_sha512_update( &ctx, input, ilen );
esp_sha512_finish( &ctx, output );
esp_sha512_free( &ctx );
}
////