/* * Copyright 2017 NXP * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * o Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fsl_dcp.h" /******************************************************************************* * Definitions ******************************************************************************/ /*! Compile time sizeof() check */ #define BUILD_ASSURE(condition, msg) extern int msg[1 - 2 * (!(condition))] __attribute__((unused)) #define dcp_memcpy memcpy /*! Internal states of the HASH creation process */ typedef enum _dcp_hash_algo_state { kDCP_StateHashInit = 1u, /*!< Init state. */ kDCP_StateHashUpdate, /*!< Update state. */ } dcp_hash_algo_state_t; /*! multiple of 64-byte block represented as byte array of 32-bit words */ typedef union _dcp_hash_block { uint32_t w[DCP_HASH_BLOCK_SIZE / 4]; /*!< array of 32-bit words */ uint8_t b[DCP_HASH_BLOCK_SIZE]; /*!< byte array */ } dcp_hash_block_t; /*! internal dcp_hash context structure */ typedef struct _dcp_hash_ctx_internal { dcp_hash_block_t blk; /*!< memory buffer. only full blocks are written to DCP during hash updates */ size_t blksz; /*!< number of valid bytes in memory buffer */ dcp_hash_algo_t algo; /*!< selected algorithm from the set of supported algorithms */ dcp_hash_algo_state_t state; /*!< finite machine state of the hash software process */ uint32_t fullMessageSize; /*!< track message size */ uint32_t ctrl0; /*!< HASH_INIT and HASH_TERM flags */ uint32_t runningHash[9]; /*!< running hash. up to SHA-256 plus size, that is 36 bytes. */ dcp_handle_t *handle; } dcp_hash_ctx_internal_t; /*!< SHA-1/SHA-2 digest length in bytes */ enum _dcp_hash_digest_len { kDCP_OutLenSha1 = 20u, kDCP_OutLenSha256 = 32u, kDCP_OutLenCrc32 = 4u, }; enum _dcp_work_packet_bit_definitions { kDCP_CONTROL0_DECR_SEMAPHOR = 1u << 1, /* DECR_SEMAPHOR */ kDCP_CONTROL0_ENABLE_HASH = 1u << 6, /* ENABLE_HASH */ kDCP_CONTROL0_HASH_INIT = 1u << 12, /* HASH_INIT */ kDCP_CONTROL0_HASH_TERM = 1u << 13, /* HASH_TERM */ kDCP_CONTROL1_HASH_SELECT_SHA256 = 2u << 16, kDCP_CONTROL1_HASH_SELECT_SHA1 = 0u << 16, kDCP_CONTROL1_HASH_SELECT_CRC32 = 1u << 16, }; /*! 64-byte block represented as byte array of 16 32-bit words */ typedef union _dcp_sha_block { uint32_t w[64 / 4]; /*!< array of 32-bit words */ uint8_t b[64]; /*!< byte array */ } dcp_sha_block_t; #if defined(DCP_HASH_CAVP_COMPATIBLE) /* result of sha1 hash for message with zero size */ static uint8_t s_nullSha1[] = {0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09}; /* result of sha256 hash for message with zero size */ static uint8_t s_nullSha256[] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; #endif /* DCP_HASH_CAVP_COMPATIBLE */ /******************************************************************************* * Variables ******************************************************************************/ static dcp_context_t s_dcpContextSwitchingBuffer; /******************************************************************************* * Code ******************************************************************************/ static void dcp_reverse_and_copy(uint8_t *src, uint8_t *dest, size_t src_len) { for (int i = 0; i < src_len; i++) { dest[i] = src[src_len - 1 - i]; } } static status_t dcp_get_channel_status(DCP_Type *base, dcp_channel_t channel) { uint32_t statReg = 0; uint32_t semaReg = 0; status_t status = kStatus_Fail; switch (channel) { case kDCP_Channel0: statReg = base->CH0STAT; semaReg = base->CH0SEMA; break; case kDCP_Channel1: statReg = base->CH1STAT; semaReg = base->CH1SEMA; break; case kDCP_Channel2: statReg = base->CH2STAT; semaReg = base->CH2SEMA; break; case kDCP_Channel3: statReg = base->CH3STAT; semaReg = base->CH3SEMA; break; default: break; } if (!((semaReg & DCP_CH0SEMA_VALUE_MASK) || (statReg & DCP_CH0STAT_ERROR_CODE_MASK))) { status = kStatus_Success; } return status; } static void dcp_clear_status(DCP_Type *base) { volatile uint32_t *dcpStatClrPtr = &base->STAT + 2u; *dcpStatClrPtr = 0xFFu; } static void dcp_clear_channel_status(DCP_Type *base, uint32_t mask) { volatile uint32_t *chStatClrPtr; if (mask & kDCP_Channel0) { chStatClrPtr = &base->CH0STAT + 2u; *chStatClrPtr = 0xFFu; } if (mask & kDCP_Channel1) { chStatClrPtr = &base->CH1STAT + 2u; *chStatClrPtr = 0xFFu; } if (mask & kDCP_Channel2) { chStatClrPtr = &base->CH2STAT + 2u; *chStatClrPtr = 0xFFu; } if (mask & kDCP_Channel3) { chStatClrPtr = &base->CH3STAT + 2u; *chStatClrPtr = 0xFFu; } } static status_t dcp_aes_set_sram_based_key(DCP_Type *base, dcp_handle_t *handle, const uint8_t *key) { base->KEY = DCP_KEY_INDEX(handle->keySlot) | DCP_KEY_SUBWORD(0); /* move the key by 32-bit words */ int i = 0; size_t keySize = 16u; while (keySize) { keySize -= sizeof(uint32_t); base->KEYDATA = ((uint32_t *)(uintptr_t)key)[i]; i++; } return kStatus_Success; } static status_t dcp_schedule_work(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket) { status_t status; /* check if our channel is active */ if ((base->STAT & (uint32_t)handle->channel) != handle->channel) { /* disable global interrupt */ uint32_t currPriMask = DisableGlobalIRQ(); /* re-check if our channel is still available */ if ((base->STAT & (uint32_t)handle->channel) == 0) { volatile uint32_t *cmdptr = NULL; volatile uint32_t *chsema = NULL; switch (handle->channel) { case kDCP_Channel0: cmdptr = &base->CH0CMDPTR; chsema = &base->CH0SEMA; break; case kDCP_Channel1: cmdptr = &base->CH1CMDPTR; chsema = &base->CH1SEMA; break; case kDCP_Channel2: cmdptr = &base->CH2CMDPTR; chsema = &base->CH2SEMA; break; case kDCP_Channel3: cmdptr = &base->CH3CMDPTR; chsema = &base->CH3SEMA; break; default: break; } if (cmdptr && chsema) { /* set out packet to DCP CMDPTR */ *cmdptr = (uint32_t)dcpPacket; /* set the channel semaphore */ *chsema = 1u; } status = kStatus_Success; } else { status = kStatus_DCP_Again; } /* global interrupt enable */ EnableGlobalIRQ(currPriMask); } else { return kStatus_DCP_Again; } return status; } status_t DCP_AES_SetKey(DCP_Type *base, dcp_handle_t *handle, const uint8_t *key, size_t keySize) { status_t status = kStatus_Fail; if ((kDCP_OtpKey == handle->keySlot) || (kDCP_OtpUniqueKey == handle->keySlot)) { /* for AES OTP and unique key, check and return read from fuses status */ if ((base->STAT & DCP_STAT_OTP_KEY_READY_MASK) == DCP_STAT_OTP_KEY_READY_MASK) { status = kStatus_Success; } } else { /* only work with aligned key[] */ if (0x3U & (uintptr_t)key) { return kStatus_InvalidArgument; } /* keySize must be 16. */ if (keySize != 16U) { return kStatus_InvalidArgument; } /* move the key by 32-bit words */ int i = 0; while (keySize) { keySize -= sizeof(uint32_t); handle->keyWord[i] = ((uint32_t *)(uintptr_t)key)[i]; i++; } if (kDCP_PayloadKey != handle->keySlot) { /* move the key by 32-bit words to DCP SRAM-based key storage */ status = dcp_aes_set_sram_based_key(base, handle, key); } else { /* for PAYLOAD_KEY, just return Ok status now */ status = kStatus_Success; } } return status; } status_t DCP_AES_EncryptEcb( DCP_Type *base, dcp_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext, size_t size) { status_t completionStatus = kStatus_Fail; dcp_work_packet_t dcpWork = {0}; do { completionStatus = DCP_AES_EncryptEcbNonBlocking(base, handle, &dcpWork, plaintext, ciphertext, size); } while (completionStatus == kStatus_DCP_Again); if (completionStatus != kStatus_Success) { return completionStatus; } return DCP_WaitForChannelComplete(base, handle); } status_t DCP_AES_EncryptEcbNonBlocking(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket, const uint8_t *plaintext, uint8_t *ciphertext, size_t size) { /* Size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } dcpPacket->control0 = 0x122u; /* CIPHER_ENCRYPT | ENABLE_CIPHER | DECR_SEMAPHORE */ dcpPacket->sourceBufferAddress = (uint32_t)plaintext; dcpPacket->destinationBufferAddress = (uint32_t)ciphertext; dcpPacket->bufferSize = (uint32_t)size; if (handle->keySlot == kDCP_OtpKey) { dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 = (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ } else if (handle->keySlot == kDCP_OtpUniqueKey) { dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 = (0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ } else if (handle->keySlot == kDCP_PayloadKey) { /* ECB does not have IV, so we can point payload directly to keyWord[] stored in handle. */ dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; dcpPacket->control0 |= (1u << 11); /* PAYLOAD_KEY */ } else { dcpPacket->control1 = (handle->keySlot << 8); /* KEY_SELECT = keySlot */ } return dcp_schedule_work(base, handle, dcpPacket); } status_t DCP_AES_DecryptEcb( DCP_Type *base, dcp_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext, size_t size) { status_t completionStatus = kStatus_Fail; dcp_work_packet_t dcpWork = {0}; do { completionStatus = DCP_AES_DecryptEcbNonBlocking(base, handle, &dcpWork, ciphertext, plaintext, size); } while (completionStatus == kStatus_DCP_Again); if (completionStatus != kStatus_Success) { return completionStatus; } return DCP_WaitForChannelComplete(base, handle); } status_t DCP_AES_DecryptEcbNonBlocking(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket, const uint8_t *ciphertext, uint8_t *plaintext, size_t size) { /* Size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } dcpPacket->control0 = 0x22u; /* ENABLE_CIPHER | DECR_SEMAPHORE */ dcpPacket->sourceBufferAddress = (uint32_t)ciphertext; dcpPacket->destinationBufferAddress = (uint32_t)plaintext; dcpPacket->bufferSize = (uint32_t)size; if (handle->keySlot == kDCP_OtpKey) { dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 = (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ } else if (handle->keySlot == kDCP_OtpUniqueKey) { dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 = (0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ } else if (handle->keySlot == kDCP_PayloadKey) { /* ECB does not have IV, so we can point payload directly to keyWord[] stored in handle. */ dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; dcpPacket->control0 |= (1u << 11); /* PAYLOAD_KEY */ } else { dcpPacket->control1 = (handle->keySlot << 8); /* KEY_SELECT = keySlot */ } return dcp_schedule_work(base, handle, dcpPacket); } status_t DCP_AES_EncryptCbc(DCP_Type *base, dcp_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext, size_t size, const uint8_t iv[16]) { status_t completionStatus = kStatus_Fail; dcp_work_packet_t dcpWork = {0}; do { completionStatus = DCP_AES_EncryptCbcNonBlocking(base, handle, &dcpWork, plaintext, ciphertext, size, iv); } while (completionStatus == kStatus_DCP_Again); if (completionStatus != kStatus_Success) { return completionStatus; } return DCP_WaitForChannelComplete(base, handle); } status_t DCP_AES_EncryptCbcNonBlocking(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket, const uint8_t *plaintext, uint8_t *ciphertext, size_t size, const uint8_t *iv) { /* Size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } dcpPacket->control0 = 0x322u; /* CIPHER_INIT | CIPHER_ENCRYPT | ENABLE_CIPHER | DECR_SEMAPHORE */ dcpPacket->control1 = 0x10u; /* CBC */ dcpPacket->sourceBufferAddress = (uint32_t)plaintext; dcpPacket->destinationBufferAddress = (uint32_t)ciphertext; dcpPacket->bufferSize = (uint32_t)size; if (handle->keySlot == kDCP_OtpKey) { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 |= (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ } else if (handle->keySlot == kDCP_OtpUniqueKey) { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 |= (0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ } else if (handle->keySlot == kDCP_PayloadKey) { /* In this case payload must contain key & iv in one array. */ /* Copy iv into handle right behind the keyWord[] so we can point payload to keyWord[]. */ dcp_memcpy(handle->iv, iv, 16); dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; dcpPacket->control0 |= (1u << 11); /* PAYLOAD_KEY */ } else { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control1 |= ((uint32_t)handle->keySlot << 8); /* KEY_SELECT = keySlot */ } return dcp_schedule_work(base, handle, dcpPacket); } status_t DCP_AES_DecryptCbc(DCP_Type *base, dcp_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext, size_t size, const uint8_t iv[16]) { status_t completionStatus = kStatus_Fail; dcp_work_packet_t dcpWork = {0}; do { completionStatus = DCP_AES_DecryptCbcNonBlocking(base, handle, &dcpWork, ciphertext, plaintext, size, iv); } while (completionStatus == kStatus_DCP_Again); if (completionStatus != kStatus_Success) { return completionStatus; } return DCP_WaitForChannelComplete(base, handle); } status_t DCP_AES_DecryptCbcNonBlocking(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket, const uint8_t *ciphertext, uint8_t *plaintext, size_t size, const uint8_t *iv) { /* Size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } dcpPacket->control0 = 0x222u; /* CIPHER_INIT | ENABLE_CIPHER | DECR_SEMAPHORE */ dcpPacket->control1 = 0x10u; /* CBC */ dcpPacket->sourceBufferAddress = (uint32_t)ciphertext; dcpPacket->destinationBufferAddress = (uint32_t)plaintext; dcpPacket->bufferSize = (uint32_t)size; if (handle->keySlot == kDCP_OtpKey) { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 |= (0xFFu << 8); /* OTP_KEY */ } else if (handle->keySlot == kDCP_OtpUniqueKey) { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ dcpPacket->control1 |= (0xFEu << 8); /* UNIQUE_KEY */ } else if (handle->keySlot == kDCP_PayloadKey) { /* in this case payload must contain KEY + IV together */ /* copy iv into handle struct so we can point payload directly to keyWord[]. */ dcp_memcpy(handle->iv, iv, 16); dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; dcpPacket->control0 |= (1u << 11); /* PAYLOAD_KEY */ } else { dcpPacket->payloadPointer = (uint32_t)iv; dcpPacket->control1 |= ((uint32_t)handle->keySlot << 8); /* KEY_SELECT */ } return dcp_schedule_work(base, handle, dcpPacket); } void DCP_GetDefaultConfig(dcp_config_t *config) { /* ENABLE_CONTEXT_CACHING is disabled by default as the DCP Hash driver uses * dcp_hash_save_running_hash() and dcp_hash_restore_running_hash() to support * Hash context switch (different messages interleaved) on the same channel. */ dcp_config_t userConfig = { true, false, true, kDCP_chEnableAll, kDCP_chIntDisable, }; *config = userConfig; } void DCP_Init(DCP_Type *base, const dcp_config_t *config) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) CLOCK_EnableClock(kCLOCK_Dcp); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ base->CTRL = 0xF0800000u; /* reset value */ base->CTRL = 0x30800000u; /* default value */ dcp_clear_status(base); dcp_clear_channel_status(base, kDCP_Channel0 | kDCP_Channel1 | kDCP_Channel2 | kDCP_Channel3); base->CTRL = DCP_CTRL_GATHER_RESIDUAL_WRITES(config->gatherResidualWrites) | DCP_CTRL_ENABLE_CONTEXT_CACHING(config->enableContextCaching) | DCP_CTRL_ENABLE_CONTEXT_SWITCHING(config->enableContextSwitching) | DCP_CTRL_CHANNEL_INTERRUPT_ENABLE(config->enableChannelInterrupt); /* enable DCP channels */ base->CHANNELCTRL = DCP_CHANNELCTRL_ENABLE_CHANNEL(config->enableChannel); /* use context switching buffer */ base->CONTEXT = (uint32_t)&s_dcpContextSwitchingBuffer; } void DCP_Deinit(DCP_Type *base) { base->CTRL = 0xF0800000u; /* reset value */ memset(&s_dcpContextSwitchingBuffer, 0, sizeof(s_dcpContextSwitchingBuffer)); #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) CLOCK_DisableClock(kCLOCK_Dcp); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } status_t DCP_WaitForChannelComplete(DCP_Type *base, dcp_handle_t *handle) { /* wait if our channel is still active */ while ((base->STAT & (uint32_t)handle->channel) == handle->channel) { } if (dcp_get_channel_status(base, handle->channel) != kStatus_Success) { dcp_clear_status(base); dcp_clear_channel_status(base, handle->channel); return kStatus_Fail; } return kStatus_Success; } /*! * @brief Check validity of algoritm. * * This function checks the validity of input argument. * * @param algo Tested algorithm value. * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. */ static status_t dcp_hash_check_input_alg(dcp_hash_algo_t algo) { if ((algo != kDCP_Sha256) && (algo != kDCP_Sha1) && (algo != kDCP_Crc32)) { return kStatus_InvalidArgument; } return kStatus_Success; } /*! * @brief Check validity of input arguments. * * This function checks the validity of input arguments. * * @param base DCP peripheral base address. * @param ctx Memory buffer given by user application where the DCP_HASH_Init/DCP_HASH_Update/DCP_HASH_Finish store * context. * @param algo Tested algorithm value. * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. */ static status_t dcp_hash_check_input_args(DCP_Type *base, dcp_hash_ctx_t *ctx, dcp_hash_algo_t algo) { /* Check validity of input algorithm */ if (kStatus_Success != dcp_hash_check_input_alg(algo)) { return kStatus_InvalidArgument; } if ((NULL == ctx) || (NULL == base)) { return kStatus_InvalidArgument; } return kStatus_Success; } /*! * @brief Check validity of internal software context. * * This function checks if the internal context structure looks correct. * * @param ctxInternal Internal context. * @param message Input message address. * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. */ static status_t dcp_hash_check_context(dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *message) { if ((NULL == message) || (NULL == ctxInternal) || (kStatus_Success != dcp_hash_check_input_alg(ctxInternal->algo))) { return kStatus_InvalidArgument; } return kStatus_Success; } /*! * @brief Initialize the SHA engine for new hash. * * This function sets kDCP_CONTROL0_HASH_INIT for control0 in work packet to start a new hash. * * @param base SHA peripheral base address. * @param ctxInternal Internal context. */ static status_t dcp_hash_engine_init(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal) { status_t status; status = kStatus_InvalidArgument; if ((kDCP_Sha256 == ctxInternal->algo) || (kDCP_Sha1 == ctxInternal->algo) || (kDCP_Crc32 == ctxInternal->algo)) { ctxInternal->ctrl0 = kDCP_CONTROL0_HASH_INIT; status = kStatus_Success; } return status; } static status_t dcp_hash_update_non_blocking( DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, dcp_work_packet_t *dcpPacket, const uint8_t *msg, size_t size) { dcpPacket->control0 = ctxInternal->ctrl0 | kDCP_CONTROL0_ENABLE_HASH | kDCP_CONTROL0_DECR_SEMAPHOR; if (ctxInternal->algo == kDCP_Sha256) { dcpPacket->control1 = kDCP_CONTROL1_HASH_SELECT_SHA256; } else if (ctxInternal->algo == kDCP_Sha1) { dcpPacket->control1 = kDCP_CONTROL1_HASH_SELECT_SHA1; } else if (ctxInternal->algo == kDCP_Crc32) { dcpPacket->control1 = kDCP_CONTROL1_HASH_SELECT_CRC32; } else { return kStatus_Fail; } dcpPacket->sourceBufferAddress = (uint32_t)msg; dcpPacket->destinationBufferAddress = 0; dcpPacket->bufferSize = size; dcpPacket->payloadPointer = (uint32_t)ctxInternal->runningHash; return dcp_schedule_work(base, ctxInternal->handle, dcpPacket); } static status_t dcp_hash_update(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *msg, size_t size) { status_t completionStatus = kStatus_Fail; dcp_work_packet_t dcpWork = {0}; do { completionStatus = dcp_hash_update_non_blocking(base, ctxInternal, &dcpWork, msg, size); } while (completionStatus == kStatus_DCP_Again); completionStatus = DCP_WaitForChannelComplete(base, ctxInternal->handle); ctxInternal->ctrl0 = 0; /* clear kDCP_CONTROL0_HASH_INIT and kDCP_CONTROL0_HASH_TERM flags */ return (completionStatus); } /*! * @brief Adds message to current hash. * * This function merges the message to fill the internal buffer, empties the internal buffer if * it becomes full, then process all remaining message data. * * * @param base DCP peripheral base address. * @param ctxInternal Internal context. * @param message Input message. * @param messageSize Size of input message in bytes. * @return kStatus_Success. */ static status_t dcp_hash_process_message_data(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *message, size_t messageSize) { status_t status = kStatus_Fail; /* if there is partially filled internal buffer, fill it to full block */ if (ctxInternal->blksz > 0) { size_t toCopy = DCP_HASH_BLOCK_SIZE - ctxInternal->blksz; dcp_memcpy(&ctxInternal->blk.b[ctxInternal->blksz], message, toCopy); message += toCopy; messageSize -= toCopy; /* process full internal block */ status = dcp_hash_update(base, ctxInternal, &ctxInternal->blk.b[0], DCP_HASH_BLOCK_SIZE); if (kStatus_Success != status) { return status; } } /* process all full blocks in message[] */ uint32_t fullBlocksSize = ((messageSize >> 6) << 6); /* (X / 64) * 64 */ if (fullBlocksSize > 0) { status = dcp_hash_update(base, ctxInternal, message, fullBlocksSize); if (kStatus_Success != status) { return status; } message += fullBlocksSize; messageSize -= fullBlocksSize; } /* copy last incomplete message bytes into internal block */ dcp_memcpy(&ctxInternal->blk.b[0], message, messageSize); ctxInternal->blksz = messageSize; return status; } /*! * @brief Finalize the running hash to make digest. * * This function empties the internal buffer, adds padding bits, and generates final digest. * * @param base SHA peripheral base address. * @param ctxInternal Internal context. * @return kStatus_Success. */ static status_t dcp_hash_finalize(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal) { status_t status; ctxInternal->ctrl0 |= kDCP_CONTROL0_HASH_TERM; status = dcp_hash_update(base, ctxInternal, &ctxInternal->blk.b[0], ctxInternal->blksz); return status; } static void dcp_hash_save_running_hash(dcp_hash_ctx_internal_t *ctxInternal) { uint32_t *srcAddr = NULL; switch (ctxInternal->handle->channel) { case kDCP_Channel0: srcAddr = &s_dcpContextSwitchingBuffer.x[43]; break; case kDCP_Channel1: srcAddr = &s_dcpContextSwitchingBuffer.x[30]; break; case kDCP_Channel2: srcAddr = &s_dcpContextSwitchingBuffer.x[17]; break; case kDCP_Channel3: srcAddr = &s_dcpContextSwitchingBuffer.x[4]; break; default: break; } if (srcAddr) { dcp_memcpy(ctxInternal->runningHash, srcAddr, sizeof(ctxInternal->runningHash)); } } static void dcp_hash_restore_running_hash(dcp_hash_ctx_internal_t *ctxInternal) { uint32_t *destAddr = NULL; switch (ctxInternal->handle->channel) { case kDCP_Channel0: destAddr = &s_dcpContextSwitchingBuffer.x[43]; break; case kDCP_Channel1: destAddr = &s_dcpContextSwitchingBuffer.x[30]; break; case kDCP_Channel2: destAddr = &s_dcpContextSwitchingBuffer.x[17]; break; case kDCP_Channel3: destAddr = &s_dcpContextSwitchingBuffer.x[4]; break; default: break; } if (destAddr) { dcp_memcpy(destAddr, ctxInternal->runningHash, sizeof(ctxInternal->runningHash)); } } status_t DCP_HASH_Init(DCP_Type *base, dcp_handle_t *handle, dcp_hash_ctx_t *ctx, dcp_hash_algo_t algo) { status_t status; dcp_hash_ctx_internal_t *ctxInternal; /* compile time check for the correct structure size */ BUILD_ASSURE(sizeof(dcp_hash_ctx_t) >= sizeof(dcp_hash_ctx_internal_t), dcp_hash_ctx_t_size); uint32_t i; status = dcp_hash_check_input_args(base, ctx, algo); if (status != kStatus_Success) { return status; } /* set algorithm in context struct for later use */ ctxInternal = (dcp_hash_ctx_internal_t *)ctx; ctxInternal->algo = algo; ctxInternal->blksz = 0u; for (i = 0; i < sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); i++) { ctxInternal->blk.w[0] = 0u; } ctxInternal->state = kDCP_StateHashInit; ctxInternal->fullMessageSize = 0; ctxInternal->handle = handle; return status; } status_t DCP_HASH_Update(DCP_Type *base, dcp_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize) { bool isUpdateState; status_t status; dcp_hash_ctx_internal_t *ctxInternal; size_t blockSize; if (inputSize == 0) { return kStatus_Success; } ctxInternal = (dcp_hash_ctx_internal_t *)ctx; status = dcp_hash_check_context(ctxInternal, input); if (kStatus_Success != status) { return status; } ctxInternal->fullMessageSize += inputSize; blockSize = DCP_HASH_BLOCK_SIZE; /* if we are still less than DCP_HASH_BLOCK_SIZE bytes, keep only in context */ if ((ctxInternal->blksz + inputSize) <= blockSize) { dcp_memcpy((&ctxInternal->blk.b[0]) + ctxInternal->blksz, input, inputSize); ctxInternal->blksz += inputSize; return status; } else { isUpdateState = ctxInternal->state == kDCP_StateHashUpdate; if (!isUpdateState) { /* start NEW hash */ status = dcp_hash_engine_init(base, ctxInternal); if (status != kStatus_Success) { return status; } ctxInternal->state = kDCP_StateHashUpdate; } else { dcp_hash_restore_running_hash(ctxInternal); } } /* process input data */ status = dcp_hash_process_message_data(base, ctxInternal, input, inputSize); dcp_hash_save_running_hash(ctxInternal); return status; } status_t DCP_HASH_Finish(DCP_Type *base, dcp_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize) { size_t algOutSize = 0; status_t status; dcp_hash_ctx_internal_t *ctxInternal; ctxInternal = (dcp_hash_ctx_internal_t *)ctx; status = dcp_hash_check_context(ctxInternal, output); if (kStatus_Success != status) { return status; } if (ctxInternal->state == kDCP_StateHashInit) { status = dcp_hash_engine_init(base, ctxInternal); if (status != kStatus_Success) { return status; } } else { dcp_hash_restore_running_hash(ctxInternal); } size_t outSize = 0u; /* compute algorithm output length */ switch (ctxInternal->algo) { case kDCP_Sha256: outSize = kDCP_OutLenSha256; break; case kDCP_Sha1: outSize = kDCP_OutLenSha1; break; case kDCP_Crc32: outSize = kDCP_OutLenCrc32; break; default: break; } algOutSize = outSize; #if defined(DCP_HASH_CAVP_COMPATIBLE) if (ctxInternal->fullMessageSize == 0) { switch (ctxInternal->algo) { case kDCP_Sha256: dcp_memcpy(&output[0], &s_nullSha256, 32); break; case kDCP_Sha1: dcp_memcpy(&output[0], &s_nullSha1, 20); break; default: break; } return kStatus_Success; } #endif /* DCP_HASH_CAVP_COMPATIBLE */ /* flush message last incomplete block, if there is any, and add padding bits */ status = dcp_hash_finalize(base, ctxInternal); if (outputSize) { if (algOutSize < *outputSize) { *outputSize = algOutSize; } else { algOutSize = *outputSize; } } /* Reverse and copy result to output[] */ dcp_reverse_and_copy((uint8_t *)ctxInternal->runningHash, &output[0], algOutSize); memset(ctx, 0, sizeof(dcp_hash_ctx_t)); return status; } status_t DCP_HASH(DCP_Type *base, dcp_handle_t *handle, dcp_hash_algo_t algo, const uint8_t *input, size_t inputSize, uint8_t *output, size_t *outputSize) { dcp_hash_ctx_t hashCtx; status_t status; status = DCP_HASH_Init(base, handle, &hashCtx, algo); if (status != kStatus_Success) { return status; } status = DCP_HASH_Update(base, &hashCtx, input, inputSize); if (status != kStatus_Success) { return status; } status = DCP_HASH_Finish(base, &hashCtx, output, outputSize); return status; }