From d96bcda60655a8b2286c8b6ef58f2618173f6808 Mon Sep 17 00:00:00 2001 From: ccli8 Date: Thu, 21 Dec 2017 14:17:15 +0800 Subject: [PATCH] [NUC472/M487] Fix indefinite loop in SHA alter. --- .../TARGET_M480/sha/sha_alt_hw.c | 23 ++++- .../TARGET_NUC472/sha/sha_alt_hw.c | 23 ++++- targets/TARGET_NUVOTON/nu_timer.h | 93 +++++++++++++++++++ 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 targets/TARGET_NUVOTON/nu_timer.h diff --git a/features/mbedtls/targets/TARGET_NUVOTON/TARGET_M480/sha/sha_alt_hw.c b/features/mbedtls/targets/TARGET_NUVOTON/TARGET_M480/sha/sha_alt_hw.c index e35a31efa3..54d63741c1 100644 --- a/features/mbedtls/targets/TARGET_NUVOTON/TARGET_M480/sha/sha_alt_hw.c +++ b/features/mbedtls/targets/TARGET_NUVOTON/TARGET_M480/sha/sha_alt_hw.c @@ -23,6 +23,7 @@ #if defined(MBEDTLS_SHA1_ALT) || defined(MBEDTLS_SHA256_ALT) || defined(MBEDTLS_SHA512_ALT) #include "nu_bitutil.h" +#include "nu_timer.h" #include "mbed_assert.h" #include "mbed_error.h" #include "crypto-misc.h" @@ -374,9 +375,20 @@ void crypto_sha_update_nobuf(crypto_sha_context *ctx, const unsigned char *input if (islast) { // Finish of last block while (CRPT->HMAC_STS & CRPT_HMAC_STS_BUSY_Msk); } else { // Finish of non-last block - // No H/W flag to indicate finish of non-last block process. - // Values of SHA_DGSTx registers will change as last word of the block is input, so use it for judgement. + /* SHA accelerator doesn't export a flag to indicate non-last block process has finished. + * Per designer, if the digest (SHA_DGSTx) code changes after the last word of the block is input, + * this indicates the non-last block process has finished. + * + * There is a rare case that two digest codes are the same for + * two non-last block processes in a row. + * To address it, we use a count-down timer to detect it. + * As the count-down timer expires, we see it as finished. + */ int isfinish = 0; + struct nu_countdown_ctx_s ctx; + + // Set up 2s timeout + nu_countdown_init(&ctx, 2000*1000); while (! isfinish) { switch (sha_opmode) { case SHA_MODE_SHA512: @@ -408,7 +420,14 @@ void crypto_sha_update_nobuf(crypto_sha_context *ctx, const unsigned char *input break; } } + + if (nu_countdown_expired(&ctx)) { + // We may meet a rare case that the current digest code and the previous one are the same. + isfinish = 1; + } } + // Must pair nu_countdown_init with nu_countdown_free in the end + nu_countdown_free(&ctx); } } diff --git a/features/mbedtls/targets/TARGET_NUVOTON/TARGET_NUC472/sha/sha_alt_hw.c b/features/mbedtls/targets/TARGET_NUVOTON/TARGET_NUC472/sha/sha_alt_hw.c index 45103bc66d..c6dc7394ac 100644 --- a/features/mbedtls/targets/TARGET_NUVOTON/TARGET_NUC472/sha/sha_alt_hw.c +++ b/features/mbedtls/targets/TARGET_NUVOTON/TARGET_NUC472/sha/sha_alt_hw.c @@ -23,6 +23,7 @@ #if defined(MBEDTLS_SHA1_ALT) || defined(MBEDTLS_SHA256_ALT) || defined(MBEDTLS_SHA512_ALT) #include "nu_bitutil.h" +#include "nu_timer.h" #include "mbed_assert.h" #include "mbed_error.h" #include "crypto-misc.h" @@ -283,9 +284,20 @@ void crypto_sha_update_nobuf(crypto_sha_context *ctx, const unsigned char *input if (islast) { // Finish of last block while (CRPT->SHA_STS & CRPT_SHA_STS_BUSY_Msk); } else { // Finish of non-last block - // No H/W flag to indicate finish of non-last block process. - // Values of SHA_DGSTx registers will change as last word of the block is input, so use it for judgement. + /* SHA accelerator doesn't export a flag to indicate non-last block process has finished. + * Per designer, if the digest (SHA_DGSTx) code changes after the last word of the block is input, + * this indicates the non-last block process has finished. + * + * There is a rare case that two digest codes are the same for + * two non-last block processes in a row. + * To address it, we use a count-down timer to detect it. + * As the count-down timer expires, we see it as finished. + */ int isfinish = 0; + struct nu_countdown_ctx_s ctx; + + // Set up 2s timeout + nu_countdown_init(&ctx, 2000*1000); while (! isfinish) { switch (sha_opmode) { case SHA_MODE_SHA256: @@ -305,7 +317,14 @@ void crypto_sha_update_nobuf(crypto_sha_context *ctx, const unsigned char *input break; } } + + if (nu_countdown_expired(&ctx)) { + // We may meet a rare case that the current digest code and the previous one are the same. + isfinish = 1; + } } + // Must pair nu_countdown_init with nu_countdown_free in the end + nu_countdown_free(&ctx); } } diff --git a/targets/TARGET_NUVOTON/nu_timer.h b/targets/TARGET_NUVOTON/nu_timer.h new file mode 100644 index 0000000000..08683370fc --- /dev/null +++ b/targets/TARGET_NUVOTON/nu_timer.h @@ -0,0 +1,93 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015-2016 Nuvoton + * + * 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 NU_TIMER_H +#define NU_TIMER_H + +#include +#include +#include "cmsis.h" +#include "mbed_sleep.h" +#include "mbed_critical.h" +#include "ticker_api.h" +#include "us_ticker_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* A simple count-down timer used for Nuvoton ported drivers + * + * NOTE: nu_countdown_init must be paired with nu_countdown_free. + * + * Example: + * nu_countdown_ctx_s ctx; + * + * // Set up 2 ms timeout + * nu_countdown_init(&ctx, 2000); + * + * // Timed-wait for a task + * while (true) { + * // Poll the task + * + * if (nu_countdown_expired(&ctx)) { + * // Timeout + * } + * } + * + * // Must pair nu_countdown_init with nu_countdown_free in the end + * nu_countdown_free(&ctx); + */ + +struct nu_countdown_ctx_s { + const ticker_data_t * _ticker_data; // Hold ticker_data_t + us_timestamp_t _interval_end_us; // End of interval in us + bool _expired; // Expired or not +}; + +__STATIC_INLINE void nu_countdown_init(struct nu_countdown_ctx_s *ctx, us_timestamp_t interval_us) +{ + core_util_critical_section_enter(); + sleep_manager_lock_deep_sleep(); + ctx->_ticker_data = get_us_ticker_data(); + ctx->_interval_end_us = ticker_read_us(ctx->_ticker_data) + interval_us; + ctx->_expired = false; + core_util_critical_section_exit(); +} + +__STATIC_INLINE bool nu_countdown_expired(struct nu_countdown_ctx_s *ctx) +{ + core_util_critical_section_enter(); + if (! ctx->_expired) { + ctx->_expired = ticker_read_us(ctx->_ticker_data) >= ctx->_interval_end_us; + } + core_util_critical_section_exit(); + + return ctx->_expired; +} + +__STATIC_INLINE void nu_countdown_free(struct nu_countdown_ctx_s *ctx) +{ + core_util_critical_section_enter(); + sleep_manager_unlock_deep_sleep(); + core_util_critical_section_exit(); +} + +#ifdef __cplusplus +} +#endif + +#endif