Skip to content

mbedtls/F439ZI: add md5 HASH feature #4160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* mbedtls_device.h
* mbedtls_device.h
*******************************************************************************
* Copyright (c) 2017, STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -22,5 +22,6 @@

#define MBEDTLS_AES_ALT

#define MBEDTLS_MD5_ALT

#endif /* MBEDTLS_DEVICE_H */
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* mbedtls_device.h
* mbedtls_device.h
*******************************************************************************
* Copyright (c) 2017, STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -22,5 +22,6 @@

#define MBEDTLS_AES_ALT

#define MBEDTLS_MD5_ALT

#endif /* MBEDTLS_DEVICE_H */
189 changes: 189 additions & 0 deletions features/mbedtls/targets/TARGET_STM/md5_alt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* MD5 hw acceleration
*******************************************************************************
* Copyright (c) 2017, STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
*
* Licensed 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.
*
*/
#if defined(MBEDTLS_MD5_C)
#include "mbedtls/md5.h"

#if defined(MBEDTLS_MD5_ALT)
#include "mbedtls/platform.h"

/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {
volatile unsigned char *p = v; while( n-- ) *p++ = 0;
}

static int st_md5_restore_hw_context(mbedtls_md5_context *ctx)
{
uint32_t i;
uint32_t tickstart;
/* allow multi-instance of HASH use: save context for HASH HW module CR */
/* Check that there is no HASH activity on going */
tickstart = HAL_GetTick();
while ((HASH->SR & (HASH_FLAG_BUSY | HASH_FLAG_DMAS)) != 0) {
if ((HAL_GetTick() - tickstart) > ST_MD5_TIMEOUT) {
return 0; // timeout: HASH processor is busy
}
}
HASH->STR = ctx->ctx_save_str;
HASH->CR = (ctx->ctx_save_cr | HASH_CR_INIT);
for (i=0;i<38;i++) {
HASH->CSR[i] = ctx->ctx_save_csr[i];
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Is it necessary here to check the status register to ensure that we are not affecting any ongoing operation of the accelerator? For example, what happens if the DMA bit is set because some data is being processed and the accelerator's state is simultaneously being changes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DMA is not used in mbed-os.
Aren't you planning some kind of mutex system later on ? It should be handled at a higher level, shouldn't it ?
You are right : I assume that there are not multiple instances of operation at the same time.

What if I cannot run the function and I cannot return any error code anyway ? Should I wait with a timeout or exit as soon as it does not work... ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @adustm
I agree that with the current framework/interface, there is not much we can do here. I think the best way is to return immediately, and record in a comment that an error code should be returned here, like you did it elsewhere in similar situation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your reply. I think to answer these planning questions it would be good to include @sbutcher-arm and @yanesca in the discussion.

return 1;
}

static int st_md5_save_hw_context(mbedtls_md5_context *ctx)
{
uint32_t i;
uint32_t tickstart;
/* Check that there is no HASH activity on going */
tickstart = HAL_GetTick();
while ((HASH->SR & (HASH_FLAG_BUSY | HASH_FLAG_DMAS)) != 0) {
if ((HAL_GetTick() - tickstart) > ST_MD5_TIMEOUT) {
return 0; // timeout: HASH processor is busy
}
}
/* allow multi-instance of HASH use: restore context for HASH HW module CR */
ctx->ctx_save_cr = HASH->CR;
ctx->ctx_save_str = HASH->STR;
for (i=0;i<38;i++) {
ctx->ctx_save_csr[i] = HASH->CSR[i];
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adustm: Thanks for making these changes. I have a couple of questions regarding this functionality though:

  • Why is it necessary to copy the CR and STR registers? I tried looking for the description of these registers in the reference manual and under CSR it states:

These registers contain the complete internal register states of the hash processor, and are
useful when a context swap has to be done because a high-priority task has to use the hash
processor while it is already in use by another task.

Therefore, my understanding is that only the values from the CSR register are required.

  • Why is it necessary to only copy the first 38 words? Shouldnt it be possible to just copy the registers HASH_CSRx that are needed or the full 54 words?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @andresag01
Here is the full content of the Reference Manual for the context swapping chapter:
Procedure where the data are loaded by software

The context can be saved only when no block is currently being processed. That is, you
must wait for DINIS = 1 (the last block has been processed and the input FIFO is empty) or
NBW ≠ 0 (the FIFO is not full and no processing is ongoing).
• Context saving:
Store the contents of the following registers into memory:
HASH_IMR
HASH_STR
HASH_CR
– HASH_CSR0 to HASH_CSR50 on STM32F415/417xx, and HASH_CSR0 to
HASH_CSR53 on STM32F43xxx.
• Context restoring:
[...]

and a note:

Note: If context swapping does not involve HMAC operations, the HASH_CSR38 to
HASH_CSR50 (STM32F415/417xx) and HASH_CSR38 to HASH_CSR53 (STM32F43xxx)
registers do not have to be saved and restored.

We are not using the interrupts in mbed-os: I did not save HASH_IMR register. But STR and CR are required to be saved-restored.

I don't save HASH_CSR38 to HASH_CSR53 because we don't use the hardware acceleration for HMAC (it is done by SW at mbedtls level)

return 1;
}

void mbedtls_md5_init( mbedtls_md5_context *ctx )
{
mbedtls_zeroize( ctx, sizeof( mbedtls_md5_context ) );

/* Enable HASH clock */
__HAL_RCC_HASH_CLK_ENABLE();

}

void mbedtls_md5_free( mbedtls_md5_context *ctx )
{
if( ctx == NULL )
return;
mbedtls_zeroize( ctx, sizeof( mbedtls_md5_context ) );
}

void mbedtls_md5_clone( mbedtls_md5_context *dst,
const mbedtls_md5_context *src )
{
*dst = *src;
}

void mbedtls_md5_starts( mbedtls_md5_context *ctx )
{
/* HASH IP initialization */
if (HAL_HASH_DeInit(&ctx->hhash_md5) != 0) {
// error found to be returned
return;
}

/* HASH Configuration */
ctx->hhash_md5.Init.DataType = HASH_DATATYPE_8B;
if (HAL_HASH_Init(&ctx->hhash_md5) != 0) {
// return error code
return;
}
if (st_md5_save_hw_context(ctx) != 1) {
return; // return HASH_BUSY timeout Error here
}
}

void mbedtls_md5_process( mbedtls_md5_context *ctx, const unsigned char data[ST_MD5_BLOCK_SIZE] )
{
if (st_md5_restore_hw_context(ctx) != 1) {
return; // Return HASH_BUSY timout error here
}
if (HAL_HASH_MD5_Accumulate(&ctx->hhash_md5, (uint8_t *)data, ST_MD5_BLOCK_SIZE) != 0) {
return; // Return error code here
}
if (st_md5_save_hw_context(ctx) != 1) {
return; // return HASH_BUSY timeout Error here
}
}

void mbedtls_md5_update( mbedtls_md5_context *ctx, const unsigned char *input, size_t ilen )
{
size_t currentlen = ilen;
if (st_md5_restore_hw_context(ctx) != 1) {
return; // Return HASH_BUSY timout error here
}
// store mechanism to accumulate ST_MD5_BLOCK_SIZE bytes (512 bits) in the HW
if (currentlen == 0){ // only change HW status is size if 0
if(ctx->hhash_md5.Phase == HAL_HASH_PHASE_READY) {
/* Select the MD5 mode and reset the HASH processor core, so that the HASH will be ready to compute
the message digest of a new message */
HASH->CR |= HASH_ALGOSELECTION_MD5 | HASH_CR_INIT;
}
ctx->hhash_md5.Phase = HAL_HASH_PHASE_PROCESS;
} else if (currentlen < (ST_MD5_BLOCK_SIZE - ctx->sbuf_len)) {
// only buffurize
memcpy(ctx->sbuf+ctx->sbuf_len, input, currentlen);
ctx->sbuf_len += currentlen;
} else {
// fill buffer and process it
memcpy(ctx->sbuf + ctx->sbuf_len, input, (ST_MD5_BLOCK_SIZE - ctx->sbuf_len));
currentlen -= (ST_MD5_BLOCK_SIZE - ctx->sbuf_len);
mbedtls_md5_process(ctx, ctx->sbuf);
// Process every input as long as it is %64 bytes, ie 512 bits
size_t iter = currentlen / ST_MD5_BLOCK_SIZE;
if (iter !=0) {
if (HAL_HASH_MD5_Accumulate(&ctx->hhash_md5, (uint8_t *)(input + ST_MD5_BLOCK_SIZE - ctx->sbuf_len), (iter * ST_MD5_BLOCK_SIZE)) != 0) {
return; // Return error code here
}
}
// sbuf is completely accumulated, now copy up to 63 remaining bytes
ctx->sbuf_len = currentlen % ST_MD5_BLOCK_SIZE;
if (ctx->sbuf_len !=0) {
memcpy(ctx->sbuf, input + ilen - ctx->sbuf_len, ctx->sbuf_len);
}
}
if (st_md5_save_hw_context(ctx) != 1) {
return; // return HASH_BUSY timeout Error here
}
}

void mbedtls_md5_finish( mbedtls_md5_context *ctx, unsigned char output[16] )
{
if (st_md5_restore_hw_context(ctx) != 1) {
return; // Return HASH_BUSY timout error here
}
if (ctx->sbuf_len > 0) {
if (HAL_HASH_MD5_Accumulate(&ctx->hhash_md5, ctx->sbuf, ctx->sbuf_len) != 0) {
return; // Return error code here
}
}
mbedtls_zeroize( ctx->sbuf, ST_MD5_BLOCK_SIZE);
ctx->sbuf_len = 0;
__HAL_HASH_START_DIGEST();

if (HAL_HASH_MD5_Finish(&ctx->hhash_md5, output, 10)) {
// error code to be returned
}
if (st_md5_save_hw_context(ctx) != 1) {
return; // return HASH_BUSY timeout Error here
}
}

#endif /* MBEDTLS_MD5_ALT */
#endif /* MBEDTLS_MD5_C */
111 changes: 111 additions & 0 deletions features/mbedtls/targets/TARGET_STM/md5_alt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* \file md5_alt.h
*
* \brief MD5 hw acceleration (hash function)
*
* Copyright (c) 2017, STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
*
* Licensed 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.
*
*/
#ifndef MBEDTLS_MD5_ALT_H
#define MBEDTLS_MD5_ALT_H

#if defined(MBEDTLS_MD5_ALT)

#include "cmsis.h"
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

#define ST_MD5_BLOCK_SIZE ((size_t)(64)) // HW handles 512 bits, ie 64 bytes
#define ST_MD5_TIMEOUT ((uint32_t) 3)

/**
* \brief MD5 context structure
* \note HAL_HASH_MD5_Accumulate will accumulate 512 bits packets, unless it is the last call to the function
* A ST_MD5_BLOCK_SIZE bytes buffer is used to save values and handle the processing
* ST_MD5_BLOCK_SIZE bytes per ST_MD5_BLOCK_SIZE bytes
* If MD5_finish is called and sbuf_len>0, the remaining bytes are accumulated prior to the call to HAL_HASH_MD5_Finish
*/
typedef struct
{
HASH_HandleTypeDef hhash_md5;/*!< ST HAL HASH struct */
unsigned char sbuf[ST_MD5_BLOCK_SIZE]; /*!< MBEDTLS_MD5_BLOCK_SIZE buffer to store values so that algorithm is caled once the buffer is filled */
unsigned char sbuf_len; /*!< number of bytes to be processed in sbuf */
uint32_t ctx_save_cr;
uint32_t ctx_save_str;
uint32_t ctx_save_csr[38];
}
mbedtls_md5_context;

/**
* \brief Initialize MD5 context
*
* \param ctx MD5 context to be initialized
*/
void mbedtls_md5_init( mbedtls_md5_context *ctx );

/**
* \brief Clear MD5 context
*
* \param ctx MD5 context to be cleared
*/
void mbedtls_md5_free( mbedtls_md5_context *ctx );

/**
* \brief Clone (the state of) an MD5 context
*
* \param dst The destination context
* \param src The context to be cloned
*/
void mbedtls_md5_clone( mbedtls_md5_context *dst,
const mbedtls_md5_context *src );

/**
* \brief MD5 context setup
*
* \param ctx context to be initialized
*/
void mbedtls_md5_starts( mbedtls_md5_context *ctx );

/**
* \brief MD5 process buffer
*
* \param ctx MD5 context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void mbedtls_md5_update( mbedtls_md5_context *ctx, const unsigned char *input, size_t ilen );

/**
* \brief MD5 final digest
*
* \param ctx MD5 context
* \param output MD5 checksum result
*/
void mbedtls_md5_finish( mbedtls_md5_context *ctx, unsigned char output[16] );

/* Internal use */
void mbedtls_md5_process( mbedtls_md5_context *ctx, const unsigned char data[ST_MD5_BLOCK_SIZE] );

#ifdef __cplusplus
}
#endif

#endif /* MBEDTLS_MD5_ALT */

#endif /* md5_alt.h */