/* * The Clear BSD License * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted (subject to the limitations in the disclaimer below) 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. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. * 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_ltc.h" /******************************************************************************* * Definitions ******************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.ltc" #endif #define LTC_FIFO_SZ_MAX_DOWN_ALGN (0xff0u) #define LTC_AES_GCM_TYPE_AAD 55 #define LTC_AES_GCM_TYPE_IV 0 #define LTC_CCM_TAG_IDX 8 /*! For CCM encryption, the encrypted final MAC is written to the context word 8-11 */ #define LTC_GCM_TAG_IDX 0 /*! For GCM encryption, the encrypted final MAC is written to the context word 0-3 */ enum _ltc_md_dk_bit_shift { kLTC_ModeRegBitShiftDK = 12U, }; /*! @brief LTC status flags */ enum _ltc_status_flag { kLTC_StatusAesBusy = 1U << LTC_STA_AB_SHIFT, #if defined(FSL_FEATURE_LTC_HAS_DES) && FSL_FEATURE_LTC_HAS_DES kLTC_StatusDesBusy = 1U << LTC_STA_DB_SHIFT, #endif /* FSL_FEATURE_LTC_HAS_DES */ #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA kLTC_StatusPkhaBusy = 1U << LTC_STA_PB_SHIFT, #endif /* FSL_FEATURE_LTC_HAS_PKHA */ #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA kLTC_StatusMdhaBusy = 1U << LTC_STA_MB_SHIFT, #endif /* FSL_FEATURE_LTC_HAS_SHA */ kLTC_StatusDoneIsr = 1U << LTC_STA_DI_SHIFT, kLTC_StatusErrorIsr = 1U << LTC_STA_EI_SHIFT, #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA kLTC_StatusPublicKeyPrime = 1U << LTC_STA_PKP_SHIFT, kLTC_StatusPublicKeyOpOne = 1U << LTC_STA_PKO_SHIFT, kLTC_StatusPublicKeyOpZero = 1U << LTC_STA_PKZ_SHIFT, #endif /* FSL_FEATURE_LTC_HAS_PKHA */ kLTC_StatusAll = LTC_STA_AB_MASK | #if defined(FSL_FEATURE_LTC_HAS_DES) && FSL_FEATURE_LTC_HAS_DES LTC_STA_DB_MASK | #endif /* FSL_FEATURE_LTC_HAS_DES */ #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA LTC_STA_MB_MASK | #endif /* FSL_FEATURE_LTC_HAS_SHA */ LTC_STA_DI_MASK | LTC_STA_EI_MASK #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA | LTC_STA_PB_MASK | LTC_STA_PKP_MASK | LTC_STA_PKO_MASK | LTC_STA_PKZ_MASK #endif /* FSL_FEATURE_LTC_HAS_PKHA */ }; /*! @brief LTC clear register */ typedef enum _ltc_clear_written { kLTC_ClearMode = 1U << LTC_CW_CM_SHIFT, kLTC_ClearDataSize = 1U << LTC_CW_CDS_SHIFT, kLTC_ClearIcvSize = 1U << LTC_CW_CICV_SHIFT, kLTC_ClearContext = 1U << LTC_CW_CCR_SHIFT, kLTC_ClearKey = 1U << LTC_CW_CKR_SHIFT, #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA kLTC_ClearPkhaSizeA = 1U << LTC_CW_CPKA_SHIFT, kLTC_ClearPkhaSizeB = 1U << LTC_CW_CPKB_SHIFT, kLTC_ClearPkhaSizeN = 1U << LTC_CW_CPKN_SHIFT, kLTC_ClearPkhaSizeE = 1U << LTC_CW_CPKE_SHIFT, kLTC_ClearAllSize = (int)kLTC_ClearPkhaSizeA | kLTC_ClearPkhaSizeB | kLTC_ClearPkhaSizeN | kLTC_ClearPkhaSizeE, #endif /* FSL_FEATURE_LTC_HAS_PKHA */ kLTC_ClearOutputFifo = 1U << LTC_CW_COF_SHIFT, kLTC_ClearInputFifo = (int)(1U << LTC_CW_CIF_SHIFT), kLTC_ClearAll = (int)(LTC_CW_CM_MASK | LTC_CW_CDS_MASK | LTC_CW_CICV_MASK | LTC_CW_CCR_MASK | LTC_CW_CKR_MASK | #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA LTC_CW_CPKA_MASK | LTC_CW_CPKB_MASK | LTC_CW_CPKN_MASK | LTC_CW_CPKE_MASK | #endif /* FSL_FEATURE_LTC_HAS_PKHA */ LTC_CW_COF_MASK | LTC_CW_CIF_MASK) } ltc_clear_written_t; enum _ltc_ctrl_swap { kLTC_CtrlSwapAll = LTC_CTL_IFS_MASK | LTC_CTL_OFS_MASK | LTC_CTL_KIS_MASK | LTC_CTL_KOS_MASK | LTC_CTL_CIS_MASK | LTC_CTL_COS_MASK, }; /*! @brief Type used in GCM and CCM modes. Content of a block is established via individual bytes and moved to LTC IFIFO by moving 32-bit words. */ typedef union _ltc_xcm_block_t { uint32_t w[4]; /*!< LTC context register is 16 bytes written as four 32-bit words */ uint8_t b[16]; /*!< 16 octets block for CCM B0 and CTR0 and for GCM */ } ltc_xcm_block_t; #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA /*! @brief PKHA functions - arithmetic, copy/clear memory. */ typedef enum _ltc_pkha_func_t { kLTC_PKHA_ClearMem = 1U, kLTC_PKHA_ArithModAdd = 2U, /*!< (A + B) mod N */ kLTC_PKHA_ArithModSub1 = 3U, /*!< (A - B) mod N */ kLTC_PKHA_ArithModSub2 = 4U, /*!< (B - A) mod N */ kLTC_PKHA_ArithModMul = 5U, /*!< (A x B) mod N */ kLTC_PKHA_ArithModExp = 6U, /*!< (A^E) mod N */ kLTC_PKHA_ArithModRed = 7U, /*!< (A) mod N */ kLTC_PKHA_ArithModInv = 8U, /*!< (A^-1) mod N */ kLTC_PKHA_ArithEccAdd = 9U, /*!< (P1 + P2) */ kLTC_PKHA_ArithEccDouble = 10U, /*!< (P2 + P2) */ kLTC_PKHA_ArithEccMul = 11U, /*!< (E x P1) */ kLTC_PKHA_ArithModR2 = 12U, /*!< (R^2 mod N) */ kLTC_PKHA_ArithGcd = 14U, /*!< GCD (A, N) */ kLTC_PKHA_ArithPrimalityTest = 15U, /*!< Miller-Rabin */ kLTC_PKHA_CopyMemSizeN = 16U, kLTC_PKHA_CopyMemSizeSrc = 17U, } ltc_pkha_func_t; /*! @brief Register areas for PKHA clear memory operations. */ typedef enum _ltc_pkha_reg_area { kLTC_PKHA_RegA = 8U, kLTC_PKHA_RegB = 4U, kLTC_PKHA_RegE = 2U, kLTC_PKHA_RegN = 1U, kLTC_PKHA_RegAll = kLTC_PKHA_RegA | kLTC_PKHA_RegB | kLTC_PKHA_RegE | kLTC_PKHA_RegN, } ltc_pkha_reg_area_t; /*! @brief Quadrant areas for 2048-bit registers for PKHA copy memory * operations. */ typedef enum _ltc_pkha_quad_area_t { kLTC_PKHA_Quad0 = 0U, kLTC_PKHA_Quad1 = 1U, kLTC_PKHA_Quad2 = 2U, kLTC_PKHA_Quad3 = 3U, } ltc_pkha_quad_area_t; /*! @brief User-supplied (R^2 mod N) input or LTC should calculate. */ typedef enum _ltc_pkha_r2_t { kLTC_PKHA_CalcR2 = 0U, /*!< Calculate (R^2 mod N) */ kLTC_PKHA_InputR2 = 1U /*!< (R^2 mod N) supplied as input */ } ltc_pkha_r2_t; /*! @brief LTC PKHA parameters */ typedef struct _ltc_pkha_mode_params_t { ltc_pkha_func_t func; ltc_pkha_f2m_t arithType; ltc_pkha_montgomery_form_t montFormIn; ltc_pkha_montgomery_form_t montFormOut; ltc_pkha_reg_area_t srcReg; ltc_pkha_quad_area_t srcQuad; ltc_pkha_reg_area_t dstReg; ltc_pkha_quad_area_t dstQuad; ltc_pkha_timing_t equalTime; ltc_pkha_r2_t r2modn; } ltc_pkha_mode_params_t; #endif /* FSL_FEATURE_LTC_HAS_PKHA */ /******************************************************************************* * Prototypes ******************************************************************************/ #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA static status_t ltc_pkha_clear_regabne(LTC_Type *base, bool A, bool B, bool N, bool E); #endif /* FSL_FEATURE_LTC_HAS_PKHA */ /******************************************************************************* * Code ******************************************************************************/ /******************************************************************************* * LTC Common code static ******************************************************************************/ /*! * @brief Tests the correct key size. * * This function tests the correct key size. * @param keySize Input key length in bytes. * @return True if the key length is supported, false if not. */ bool ltc_check_key_size(const uint32_t keySize) { return ((keySize == 16u) #if defined(FSL_FEATURE_LTC_HAS_AES192) && FSL_FEATURE_LTC_HAS_AES192 || ((keySize == 24u)) #endif /* FSL_FEATURE_LTC_HAS_AES192 */ #if defined(FSL_FEATURE_LTC_HAS_AES256) && FSL_FEATURE_LTC_HAS_AES256 || ((keySize == 32u)) #endif /* FSL_FEATURE_LTC_HAS_AES256 */ ); } /*! @brief LTC driver wait mechanism. */ status_t ltc_wait(LTC_Type *base) { status_t status; bool error = false; bool done = false; /* Wait for 'done' or 'error' flag. */ while ((!error) && (!done)) { uint32_t temp32 = base->STA; error = temp32 & LTC_STA_EI_MASK; done = temp32 & LTC_STA_DI_MASK; } if (error) { base->COM = LTC_COM_ALL_MASK; /* Reset all engine to clear the error flag */ status = kStatus_Fail; } else /* 'done' */ { status = kStatus_Success; base->CW = kLTC_ClearDataSize; /* Clear 'done' interrupt status. This also clears the mode register. */ base->STA = kLTC_StatusDoneIsr; } return status; } /*! * @brief Clears the LTC module. * This function can be used to clear all sensitive data from theLTC module, such as private keys. It is called * internally by the LTC driver in case of an error or operation complete. * @param base LTC peripheral base address * @param pkha Include LTC PKHA register clear. If there is no PKHA, the argument is ignored. */ void ltc_clear_all(LTC_Type *base, bool addPKHA) { base->CW = (uint32_t)kLTC_ClearAll; #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA if (addPKHA) { ltc_pkha_clear_regabne(base, true, true, true, true); } #endif /* FSL_FEATURE_LTC_HAS_PKHA */ } void ltc_memcpy(void *dst, const void *src, size_t size) { #if defined(__cplusplus) register uint8_t *to = (uint8_t *)dst; register const uint8_t *from = (const uint8_t *)src; #else register uint8_t *to = dst; register const uint8_t *from = src; #endif while (size) { *to = *from; size--; to++; from++; } } /*! * @brief Reads an unaligned word. * * This function creates a 32-bit word from an input array of four bytes. * * @param src Input array of four bytes. The array can start at any address in memory. * @return 32-bit unsigned int created from the input byte array. */ /* Force lower optimization for Keil, otherwise it replaces inline LDR with LDM */ #if defined(__CC_ARM) #pragma push #pragma O0 #endif static inline uint32_t ltc_get_word_from_unaligned(const uint8_t *srcAddr) { #if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0))) register const uint8_t *src = srcAddr; /* Cortex M0 does not support misaligned loads */ if ((uint32_t)src & 0x3u) { union _align_bytes_t { uint32_t word; uint8_t byte[sizeof(uint32_t)]; } my_bytes; my_bytes.byte[0] = *src; my_bytes.byte[1] = *(src + 1); my_bytes.byte[2] = *(src + 2); my_bytes.byte[3] = *(src + 3); return my_bytes.word; } else { /* addr aligned to 0-modulo-4 so it is safe to type cast */ return *((const uint32_t *)src); } #elif defined(__CC_ARM) /* -O3 optimization in Keil 5.15 and 5.16a uses LDM instruction here (LDM r4!, {r0}) * which is wrong, because srcAddr might be unaligned. * LDM on unaligned address causes hard-fault. in contrary, * LDR supports unaligned address on Cortex M4 */ register uint32_t retVal; __asm { LDR retVal, [srcAddr] } return retVal; #else return *((const uint32_t *)srcAddr); #endif } /* End lower optimization */ #if defined(__CC_ARM) #pragma pop #endif /*! * @brief Converts a 32-bit word into a byte array. * * This function creates an output array of four bytes from an input 32-bit word. * * @param srcWord Input 32-bit unsigned integer. * @param dst Output array of four bytes. The array can start at any address in memory. */ static inline void ltc_set_unaligned_from_word(uint32_t srcWord, uint8_t *dstAddr) { #if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0))) register uint8_t *dst = dstAddr; /* Cortex M0 does not support misaligned stores */ if ((uint32_t)dst & 0x3u) { *dst++ = (srcWord & 0x000000FFU); *dst++ = (srcWord & 0x0000FF00U) >> 8; *dst++ = (srcWord & 0x00FF0000U) >> 16; *dst++ = (srcWord & 0xFF000000U) >> 24; } else { *((uint32_t *)dstAddr) = srcWord; /* addr aligned to 0-modulo-4 so it is safe to type cast */ } #elif defined(__CC_ARM) __asm { STR srcWord, [dstAddr] } return; #else *((uint32_t *)dstAddr) = srcWord; #endif } /*! * @brief Sets the LTC keys. * * This function writes the LTC keys into the key register. The keys should * be written before the key size. * * @param base LTC peripheral base address * @param key Key * @param keySize Number of bytes for all keys to be loaded (maximum 32, must be a * multiple of 4). * @returns Key set status */ static status_t ltc_set_key(LTC_Type *base, const uint8_t *key, uint8_t keySize) { int32_t i; for (i = 0; i < (keySize / 4); i++) { base->KEY[i] = ltc_get_word_from_unaligned(key + i * sizeof(uint32_t)); } return kStatus_Success; } /*! * @brief Gets the LTC keys. * * This function retrieves the LTC keys from the key register. * * @param base LTC peripheral base address * @param key Array of data to store keys * @param keySize Number of bytes of keys to retrieve * @returns Key set status */ static status_t ltc_get_key(LTC_Type *base, uint8_t *key, uint8_t keySize) { int32_t i; for (i = 0; i < (keySize / 4); i++) { ltc_set_unaligned_from_word(base->KEY[i], key + i * sizeof(uint32_t)); } return kStatus_Success; } /*! * @brief Writes the LTC context register; * * The LTC context register is a 512 bit (64 byte) register that holds * internal context for the crypto engine. The meaning varies based on the * algorithm and operating state being used. This register is written by the * driver/application to load state such as IV, counter, and so on. Then, it is * updated by the internal crypto engine as needed. * * @param base LTC peripheral base address * @param data Data to write * @param dataSize Size of data to write in bytes * @param startIndex Starting word (4-byte) index into the 16-word register. * @return Status of write */ status_t ltc_set_context(LTC_Type *base, const uint8_t *data, uint8_t dataSize, uint8_t startIndex) { int32_t i; int32_t j; int32_t szLeft; /* Context register is 16 words in size (64 bytes). Ensure we are only * writing a valid amount of data. */ if (startIndex + (dataSize / 4) >= 16) { return kStatus_InvalidArgument; } j = 0; szLeft = dataSize % 4; for (i = startIndex; i < (startIndex + dataSize / 4); i++) { base->CTX[i] = ltc_get_word_from_unaligned(data + j); j += sizeof(uint32_t); } if (szLeft) { uint32_t context_data = {0}; ltc_memcpy(&context_data, data + j, szLeft); base->CTX[i] = context_data; } return kStatus_Success; } /*! * @brief Reads the LTC context register. * * The LTC context register is a 512 bit (64 byte) register that holds * internal context for the crypto engine. The meaning varies based on the * algorithm and operating state being used. This register is written by the * driver/application to load state such as IV, counter, and so on. Then, it is * updated by the internal crypto engine as needed. * * @param base LTC peripheral base address * @param data Destination of read data * @param dataSize Size of data to read in bytes * @param startIndex Starting word (4-byte) index into the 16-word register. * @return Status of read */ status_t ltc_get_context(LTC_Type *base, uint8_t *dest, uint8_t dataSize, uint8_t startIndex) { int32_t i; int32_t j; int32_t szLeft; uint32_t rdCtx; /* Context register is 16 words in size (64 bytes). Ensure we are only * writing a valid amount of data. */ if (startIndex + (dataSize / 4) >= 16) { return kStatus_InvalidArgument; } j = 0; szLeft = dataSize % 4; for (i = startIndex; i < (startIndex + dataSize / 4); i++) { ltc_set_unaligned_from_word(base->CTX[i], dest + j); j += sizeof(uint32_t); } if (szLeft) { rdCtx = 0; rdCtx = base->CTX[i]; ltc_memcpy(dest + j, &rdCtx, szLeft); } return kStatus_Success; } static status_t ltc_symmetric_alg_state(LTC_Type *base, const uint8_t *key, uint8_t keySize, ltc_algorithm_t alg, ltc_mode_symmetric_alg_t mode, ltc_mode_encrypt_t enc, ltc_mode_algorithm_state_t as) { ltc_mode_t modeReg; /* Clear internal register states. */ base->CW = (uint32_t)kLTC_ClearAll; /* Set byte swap on for several registers we will be reading and writing * user data to/from. */ base->CTL |= kLTC_CtrlSwapAll; /* Write the key in place. */ ltc_set_key(base, key, keySize); /* Write the key size. This must be done after writing the key, and this * action locks the ability to modify the key registers. */ base->KS = keySize; /* Clear the 'done' interrupt. */ base->STA = kLTC_StatusDoneIsr; /* Set the proper block and algorithm mode. */ modeReg = (uint32_t)alg | (uint32_t)enc | (uint32_t)as | (uint32_t)mode; /* Write the mode register to the hardware. */ base->MD = modeReg; return kStatus_Success; } /*! * @brief Initializes the LTC for symmetric encrypt/decrypt operation. Mode is set to UPDATE. * * @param base LTC peripheral base address * @param key Input key to use for encryption * @param keySize Size of the input key, in bytes. Must be 8, 16, 24, or 32. * @param alg Symmetric algorithm * @param mode Symmetric block mode * @param enc Encrypt/decrypt control * @return Status */ status_t ltc_symmetric_update(LTC_Type *base, const uint8_t *key, uint8_t keySize, ltc_algorithm_t alg, ltc_mode_symmetric_alg_t mode, ltc_mode_encrypt_t enc) { return ltc_symmetric_alg_state(base, key, keySize, alg, mode, enc, kLTC_ModeUpdate); } #if defined(FSL_FEATURE_LTC_HAS_GCM) && FSL_FEATURE_LTC_HAS_GCM /*! * @brief Initializes the LTC for symmetric encrypt/decrypt operation. Mode is set to FINALIZE. * * @param base LTC peripheral base address * @param key Input key to use for encryption * @param keySize Size of the input key, in bytes. Must be 8, 16, 24, or 32. * @param alg Symmetric algorithm * @param mode Symmetric block mode * @param enc Encrypt/decrypt control * @return Status */ static status_t ltc_symmetric_final(LTC_Type *base, const uint8_t *key, uint8_t keySize, ltc_algorithm_t alg, ltc_mode_symmetric_alg_t mode, ltc_mode_encrypt_t enc) { return ltc_symmetric_alg_state(base, key, keySize, alg, mode, enc, kLTC_ModeFinalize); } #endif /* FSL_FEATURE_LTC_HAS_GCM */ /*! * @brief Initializes the LTC for symmetric encrypt/decrypt operation. Mode is set to INITIALIZE. * * @param base LTC peripheral base address * @param key Input key to use for encryption * @param keySize Size of the input key, in bytes. Must be 8, 16, 24, or 32. * @param alg Symmetric algorithm * @param mode Symmetric block mode * @param enc Encrypt/decrypt control * @return Status */ static status_t ltc_symmetric_init(LTC_Type *base, const uint8_t *key, uint8_t keySize, ltc_algorithm_t alg, ltc_mode_symmetric_alg_t mode, ltc_mode_encrypt_t enc) { return ltc_symmetric_alg_state(base, key, keySize, alg, mode, enc, kLTC_ModeInit); } /*! * @brief Initializes the LTC for symmetric encrypt/decrypt operation. Mode is set to INITIALIZE/FINALIZE. * * @param base LTC peripheral base address * @param key Input key to use for encryption * @param keySize Size of the input key, in bytes. Must be 8, 16, 24, or 32. * @param alg Symmetric algorithm * @param mode Symmetric block mode * @param enc Encrypt/decrypt control * @return Status */ static status_t ltc_symmetric_init_final(LTC_Type *base, const uint8_t *key, uint8_t keySize, ltc_algorithm_t alg, ltc_mode_symmetric_alg_t mode, ltc_mode_encrypt_t enc) { return ltc_symmetric_alg_state(base, key, keySize, alg, mode, enc, kLTC_ModeInitFinal); } void ltc_symmetric_process(LTC_Type *base, uint32_t inSize, const uint8_t **inData, uint8_t **outData) { uint32_t outSize; uint32_t fifoData; uint32_t fifoStatus; register const uint8_t *in = *inData; register uint8_t *out = *outData; outSize = inSize; while ((outSize > 0) || (inSize > 0)) { fifoStatus = base->FIFOSTA; /* Check output FIFO level to make sure there is at least an entry * ready to be read. */ if (fifoStatus & LTC_FIFOSTA_OFL_MASK) { /* Read data from the output FIFO. */ if (outSize > 0) { if (outSize >= sizeof(uint32_t)) { ltc_set_unaligned_from_word(base->OFIFO, out); out += sizeof(uint32_t); outSize -= sizeof(uint32_t); } else /* (outSize > 0) && (outSize < 4) */ { fifoData = base->OFIFO; ltc_memcpy(out, &fifoData, outSize); out += outSize; outSize = 0; } } } /* Check input FIFO status to see if it is full. We can * only write more data when both input and output FIFOs are not at a full state. * At the same time we are sure Output FIFO is not full because we have poped at least one entry * by the while loop above. */ if (!(fifoStatus & LTC_FIFOSTA_IFF_MASK)) { /* Copy data to the input FIFO. * Data can only be copied one word at a time, so pad the data * appropriately if it is less than this size. */ if (inSize > 0) { if (inSize >= sizeof(uint32_t)) { base->IFIFO = ltc_get_word_from_unaligned(in); inSize -= sizeof(uint32_t); in += sizeof(uint32_t); } else /* (inSize > 0) && (inSize < 4) */ { fifoData = 0; ltc_memcpy(&fifoData, in, inSize); base->IFIFO = fifoData; in += inSize; inSize = 0; } } } } *inData = in; *outData = out; } /*! * @brief Processes symmetric data through LTC AES and DES engines. * * @param base LTC peripheral base address * @param inData Input data * @param inSize Size of input data, in bytes * @param outData Output data * @return Status from encrypt/decrypt operation */ status_t ltc_symmetric_process_data(LTC_Type *base, const uint8_t *inData, uint32_t inSize, uint8_t *outData) { uint32_t lastSize; if ((!inData) || (!outData)) { return kStatus_InvalidArgument; } /* Write the data size. */ base->DS = inSize; /* Split the inSize into full 16-byte chunks and last incomplete block due to LTC AES OFIFO errata */ if (inSize <= 16u) { lastSize = inSize; inSize = 0; } else { /* Process all 16-byte data chunks. */ lastSize = inSize % 16u; if (lastSize == 0) { lastSize = 16; inSize -= 16; } else { inSize -= lastSize; /* inSize will be rounded down to 16 byte boundary. remaining bytes in lastSize */ } } ltc_symmetric_process(base, inSize, &inData, &outData); ltc_symmetric_process(base, lastSize, &inData, &outData); return ltc_wait(base); } /*! * @brief Splits the LTC job into sessions. Used for CBC, CTR, CFB, OFB cipher block modes. * * @param base LTC peripheral base address * @param inData Input data to process. * @param inSize Input size of the input buffer. * @param outData Output data buffer. */ static status_t ltc_process_message_in_sessions(LTC_Type *base, const uint8_t *inData, uint32_t inSize, uint8_t *outData) { uint32_t sz; status_t retval; ltc_mode_t modeReg; /* read and write LTC mode register */ sz = LTC_FIFO_SZ_MAX_DOWN_ALGN; modeReg = base->MD; retval = kStatus_Success; while (inSize) { if (inSize <= sz) { retval = ltc_symmetric_process_data(base, inData, inSize, outData); if (kStatus_Success != retval) { return retval; } inSize = 0; } else { retval = ltc_symmetric_process_data(base, inData, sz, outData); if (kStatus_Success != retval) { return retval; } inData += sz; inSize -= sz; outData += sz; base->MD = modeReg; } } return retval; } static void ltc_move_block_to_ififo(LTC_Type *base, const ltc_xcm_block_t *blk, uint32_t num_bytes) { uint32_t i = 0; uint32_t words; words = num_bytes / 4u; if (num_bytes % 4u) { words++; } if (words > 4) { words = 4; } while (i < words) { if (0U == (base->FIFOSTA & LTC_FIFOSTA_IFF_MASK)) { /* Copy data to the input FIFO. */ base->IFIFO = blk->w[i++]; } } } static void ltc_move_to_ififo(LTC_Type *base, const uint8_t *data, uint32_t dataSize) { ltc_xcm_block_t blk; ltc_xcm_block_t blkZero = {{0x0u, 0x0u, 0x0u, 0x0u}}; while (dataSize) { if (dataSize > 16u) { ltc_memcpy(&blk, data, 16u); dataSize -= 16u; data += 16u; } else { ltc_memcpy(&blk, &blkZero, sizeof(ltc_xcm_block_t)); /* memset blk to zeroes */ ltc_memcpy(&blk, data, dataSize); dataSize = 0; } ltc_move_block_to_ififo(base, &blk, sizeof(ltc_xcm_block_t)); } } /*! * @brief Processes symmetric data through LTC AES in multiple sessions. * * Specific for AES CCM and GCM modes as they need to update mode register. * * @param base LTC peripheral base address * @param inData Input data * @param inSize Size of input data, in bytes * @param outData Output data * @param lastAs The LTC Algorithm state to be set sup for last block during message processing in multiple sessions. * For CCM it is kLTC_ModeFinalize. For GCM it is kLTC_ModeInitFinal. * @return Status from encrypt/decrypt operation */ static status_t ltc_symmetric_process_data_multiple(LTC_Type *base, const uint8_t *inData, uint32_t inSize, uint8_t *outData, ltc_mode_t modeReg, ltc_mode_algorithm_state_t lastAs) { uint32_t fifoConsumed; uint32_t lastSize; uint32_t sz; uint32_t max_ltc_fifo_size; ltc_mode_algorithm_state_t fsm; status_t status; if ((!inData) || (!outData)) { return kStatus_InvalidArgument; } if (!((kLTC_ModeFinalize == lastAs) || (kLTC_ModeInitFinal == lastAs))) { return kStatus_InvalidArgument; } if (0 == inSize) { return kStatus_Success; } if (inSize <= 16u) { fsm = lastAs; lastSize = inSize; } else { fsm = (ltc_mode_algorithm_state_t)( modeReg & LTC_MD_AS_MASK); /* this will be either kLTC_ModeInit or kLTC_ModeUpdate, based on prior processing */ /* Process all 16-byte data chunks. */ lastSize = inSize % 16u; if (lastSize == 0u) { lastSize = 16u; inSize -= 16u; } else { inSize -= lastSize; /* inSize will be rounded down to 16 byte boundary. remaining bytes in lastSize */ } } max_ltc_fifo_size = LTC_FIFO_SZ_MAX_DOWN_ALGN; fifoConsumed = base->DS; while (lastSize) { switch (fsm) { case kLTC_ModeUpdate: case kLTC_ModeInit: while (inSize) { if (inSize > (max_ltc_fifo_size - fifoConsumed)) { sz = (max_ltc_fifo_size - fifoConsumed); } else { sz = inSize; } base->DS = sz; ltc_symmetric_process(base, sz, &inData, &outData); inSize -= sz; fifoConsumed = 0; /* after we completed INITIALIZE job, are there still any data left? */ if (inSize) { fsm = kLTC_ModeUpdate; status = ltc_wait(base); if (kStatus_Success != status) { return status; } modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)fsm; base->MD = modeReg; } else { fsm = lastAs; } } break; case kLTC_ModeFinalize: case kLTC_ModeInitFinal: /* process last block in FINALIZE */ status = ltc_wait(base); if (kStatus_Success != status) { return status; } modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)lastAs; base->MD = modeReg; base->DS = lastSize; ltc_symmetric_process(base, lastSize, &inData, &outData); lastSize = 0; break; default: break; } } status = ltc_wait(base); return status; } /*! * @brief Receives MAC compare. * * This function is a sub-process of CCM and GCM decryption. * It compares received MAC with the MAC computed during decryption. * * @param base LTC peripheral base address * @param tag Received MAC. * @param tagSize Number of bytes in the received MAC. * @param modeReg LTC Mode Register current value. It is modified and written to LTC Mode Register. */ static status_t ltc_aes_received_mac_compare(LTC_Type *base, const uint8_t *tag, uint32_t tagSize, ltc_mode_t modeReg) { ltc_xcm_block_t blk = {{0x0u, 0x0u, 0x0u, 0x0u}}; base->CW = kLTC_ClearDataSize; base->STA = kLTC_StatusDoneIsr; modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)kLTC_ModeUpdate | LTC_MD_ICV_TEST_MASK; base->MD = modeReg; base->DS = 0u; base->ICVS = tagSize; ltc_memcpy(&blk.b[0], &tag[0], tagSize); ltc_move_block_to_ififo(base, &blk, tagSize); return ltc_wait(base); } /*! * @brief Processes tag during AES GCM and CCM. * * This function is a sub-process of CCM and GCM encryption and decryption. * For encryption, it writes computed MAC to the output tag. * For decryption, it compares the received MAC with the computed MAC. * * @param base LTC peripheral base address * @param[in,out] tag Output computed MAC during encryption or Input received MAC during decryption. * @param tagSize Size of MAC buffer in bytes. * @param modeReg LTC Mode Register current value. It is checked to read Enc/Dec bit. * It is modified and written to LTC Mode Register during decryption. * @param ctx Index to LTC context registers with computed MAC for encryption process. */ static status_t ltc_aes_process_tag(LTC_Type *base, uint8_t *tag, uint32_t tagSize, ltc_mode_t modeReg, uint32_t ctx) { status_t status = kStatus_Success; if (tag) { /* For decrypt, compare received MAC with the computed MAC. */ if (kLTC_ModeDecrypt == (modeReg & LTC_MD_ENC_MASK)) { status = ltc_aes_received_mac_compare(base, tag, tagSize, modeReg); } else /* FSL_AES_GCM_TYPE_ENCRYPT */ { /* For encryption, write the computed and encrypted MAC to user buffer */ ltc_get_context(base, &tag[0], tagSize, ctx); } } return status; } /******************************************************************************* * LTC Common code public ******************************************************************************/ void LTC_Init(LTC_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* ungate clock */ CLOCK_EnableClock(kCLOCK_Ltc0); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } void LTC_Deinit(LTC_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* gate clock */ CLOCK_DisableClock(kCLOCK_Ltc0); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } #if defined(FSL_FEATURE_LTC_HAS_DPAMS) && FSL_FEATURE_LTC_HAS_DPAMS void LTC_SetDpaMaskSeed(LTC_Type *base, uint32_t mask) { base->DPAMS = mask; /* second write as workaround for DPA mask re-seed errata */ base->DPAMS = mask; } #endif /* FSL_FEATURE_LTC_HAS_DPAMS */ /******************************************************************************* * AES Code static ******************************************************************************/ static status_t ltc_aes_decrypt_ecb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t *key, uint32_t keySize, ltc_aes_key_t keyType) { status_t retval; /* Initialize algorithm state. */ ltc_symmetric_update(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeECB, kLTC_ModeDecrypt); /* set DK bit in the LTC Mode Register AAI field for directly loaded decrypt keys */ if (keyType == kLTC_DecryptKey) { base->MD |= (1U << kLTC_ModeRegBitShiftDK); } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &ciphertext[0], size, &plaintext[0]); return retval; } /******************************************************************************* * AES Code public ******************************************************************************/ status_t LTC_AES_GenerateDecryptKey(LTC_Type *base, const uint8_t *encryptKey, uint8_t *decryptKey, uint32_t keySize) { uint8_t plaintext[LTC_AES_BLOCK_SIZE]; uint8_t ciphertext[LTC_AES_BLOCK_SIZE]; status_t status; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* ECB decrypt with encrypt key will convert the key in LTC context into decrypt form of the key */ status = ltc_aes_decrypt_ecb(base, ciphertext, plaintext, LTC_AES_BLOCK_SIZE, encryptKey, keySize, kLTC_EncryptKey); /* now there is decrypt form of the key in the LTC context, so take it */ ltc_get_key(base, decryptKey, keySize); ltc_clear_all(base, false); return status; } status_t LTC_AES_EncryptEcb( LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t *key, uint32_t keySize) { status_t retval; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* ECB mode, size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ ltc_symmetric_update(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeECB, kLTC_ModeEncrypt); /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &plaintext[0], size, &ciphertext[0]); ltc_clear_all(base, false); return retval; } status_t LTC_AES_DecryptEcb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t *key, uint32_t keySize, ltc_aes_key_t keyType) { status_t status; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* ECB mode, size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } status = ltc_aes_decrypt_ecb(base, ciphertext, plaintext, size, key, keySize, keyType); ltc_clear_all(base, false); return status; } status_t LTC_AES_EncryptCbc(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_AES_IV_SIZE], const uint8_t *key, uint32_t keySize) { status_t retval; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* CBC mode, size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ ltc_symmetric_update(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeCBC, kLTC_ModeEncrypt); /* Write IV data to the context register. */ ltc_set_context(base, &iv[0], LTC_AES_IV_SIZE, 0); /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &plaintext[0], size, &ciphertext[0]); ltc_clear_all(base, false); return retval; } status_t LTC_AES_DecryptCbc(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_AES_IV_SIZE], const uint8_t *key, uint32_t keySize, ltc_aes_key_t keyType) { status_t retval; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* CBC mode, size must be 16-byte multiple */ if ((size < 16u) || (size % 16u)) { return kStatus_InvalidArgument; } /* set DK bit in the LTC Mode Register AAI field for directly loaded decrypt keys */ if (keyType == kLTC_DecryptKey) { base->MD |= (1U << kLTC_ModeRegBitShiftDK); } /* Initialize algorithm state. */ ltc_symmetric_update(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeCBC, kLTC_ModeDecrypt); /* Write IV data to the context register. */ ltc_set_context(base, &iv[0], LTC_AES_IV_SIZE, 0); /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &ciphertext[0], size, &plaintext[0]); ltc_clear_all(base, false); return retval; } status_t LTC_AES_CryptCtr(LTC_Type *base, const uint8_t *input, uint8_t *output, uint32_t size, uint8_t counter[LTC_AES_BLOCK_SIZE], const uint8_t *key, uint32_t keySize, uint8_t counterlast[LTC_AES_BLOCK_SIZE], uint32_t *szLeft) { status_t retval; uint32_t lastSize; if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } lastSize = 0U; if (counterlast != NULL) { /* Split the size into full 16-byte chunks and last incomplete block due to LTC AES OFIFO errata */ if (size <= 16U) { lastSize = size; size = 0U; } else { /* Process all 16-byte data chunks. */ lastSize = size % 16U; if (lastSize == 0U) { lastSize = 16U; size -= 16U; } else { size -= lastSize; /* size will be rounded down to 16 byte boundary. remaining bytes in lastSize */ } } } /* Initialize algorithm state. */ ltc_symmetric_update(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeCTR, kLTC_ModeEncrypt); /* Write initial counter data to the context register. * NOTE the counter values start at 4-bytes offset into the context. */ ltc_set_context(base, &counter[0], 16U, 4U); /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, input, size, output); if (kStatus_Success != retval) { return retval; } input += size; output += size; if ((counterlast != NULL) && lastSize) { uint8_t zeroes[16] = {0}; ltc_mode_t modeReg; modeReg = (uint32_t)kLTC_AlgorithmAES | (uint32_t)kLTC_ModeCTR | (uint32_t)kLTC_ModeEncrypt; /* Write the mode register to the hardware. */ base->MD = modeReg | (uint32_t)kLTC_ModeFinalize; /* context is re-used (CTRi) */ /* Process data and return status. */ retval = ltc_symmetric_process_data(base, input, lastSize, output); if (kStatus_Success != retval) { return retval; } if (szLeft) { *szLeft = 16U - lastSize; } /* Initialize algorithm state. */ base->MD = modeReg | (uint32_t)kLTC_ModeUpdate; /* context is re-used (CTRi) */ /* Process data and return status. */ retval = ltc_symmetric_process_data(base, zeroes, 16U, counterlast); } ltc_get_context(base, &counter[0], 16U, 4U); ltc_clear_all(base, false); return retval; } #if defined(FSL_FEATURE_LTC_HAS_GCM) && FSL_FEATURE_LTC_HAS_GCM /******************************************************************************* * GCM Code static ******************************************************************************/ static status_t ltc_aes_gcm_check_input_args(LTC_Type *base, const uint8_t *src, const uint8_t *iv, const uint8_t *aad, const uint8_t *key, uint8_t *dst, uint32_t inputSize, uint32_t ivSize, uint32_t aadSize, uint32_t keySize, uint32_t tagSize) { if (!base) { return kStatus_InvalidArgument; } /* tag can be NULL to skip tag processing */ if ((!key) || (ivSize && (!iv)) || (aadSize && (!aad)) || (inputSize && ((!src) || (!dst)))) { return kStatus_InvalidArgument; } /* octet length of tag (tagSize) must be element of 4,8,12,13,14,15,16 */ if (((tagSize > 16u) || (tagSize < 12u)) && (tagSize != 4u) && (tagSize != 8u)) { return kStatus_InvalidArgument; } /* check if keySize is supported */ if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* no IV AAD DATA makes no sense */ if (0 == (inputSize + ivSize + aadSize)) { return kStatus_InvalidArgument; } return kStatus_Success; } /*! * @brief Process Wrapper for void (*pfunc)(LTC_Type*, uint32_t, bool). Sets IV Size register. */ static void ivsize_next(LTC_Type *base, uint32_t ivSize, bool iv_only) { base->IVSZ = LTC_IVSZ_IL(iv_only) | ((ivSize)<C_DS_DS_MASK); } /*! * @brief Process Wrapper for void (*pfunc)(LTC_Type*, uint32_t, bool). Sets AAD Size register. */ static void aadsize_next(LTC_Type *base, uint32_t aadSize, bool aad_only) { base->AADSZ = LTC_AADSZ_AL(aad_only) | ((aadSize)<C_DS_DS_MASK); } /*! * @brief Process IV or AAD string in multi-session. * * @param base LTC peripheral base address * @param iv IV or AAD data * @param ivSize Size in bytes of IV or AAD data * @param modeReg LTC peripheral Mode register value * @param iv_only IV only or AAD only flag * @param type selects between IV or AAD */ static status_t ltc_aes_gcm_process_iv_aad( LTC_Type *base, const uint8_t *iv, uint32_t ivSize, ltc_mode_t modeReg, bool iv_only, int type, ltc_mode_t modeLast) { uint32_t sz; status_t retval; void (*next_size_func)(LTC_Type *ltcBase, uint32_t nextSize, bool authOnly); if ((NULL == iv) || (ivSize == 0)) { return kStatus_InvalidArgument; } sz = LTC_FIFO_SZ_MAX_DOWN_ALGN; next_size_func = type == LTC_AES_GCM_TYPE_AAD ? aadsize_next : ivsize_next; while (ivSize) { if (ivSize < sz) { modeReg &= ~LTC_MD_AS_MASK; modeReg |= modeLast; base->MD = modeReg; next_size_func(base, ivSize, iv_only); ltc_move_to_ififo(base, iv, ivSize); ivSize = 0; } else { /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= kLTC_ModeUpdate; base->MD = modeReg; next_size_func(base, (uint16_t)sz, true); ltc_move_to_ififo(base, iv, sz); ivSize -= sz; iv += sz; } retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } } /* end while */ return kStatus_Success; } static status_t ltc_aes_gcm_process(LTC_Type *base, ltc_mode_encrypt_t encryptMode, const uint8_t *src, uint32_t inputSize, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, uint8_t *dst, uint8_t *tag, uint32_t tagSize) { status_t retval; /* return value */ uint32_t max_ltc_fifo_sz; /* maximum data size that we can put to LTC FIFO in one session. 12-bit limit. */ ltc_mode_t modeReg; /* read and write LTC mode register */ bool single_ses_proc_all; /* iv, aad and src data can be processed in one session */ bool iv_only; bool aad_only; retval = ltc_aes_gcm_check_input_args(base, src, iv, aad, key, dst, inputSize, ivSize, aadSize, keySize, tagSize); /* API input validation */ if (kStatus_Success != retval) { return retval; } max_ltc_fifo_sz = LTC_DS_DS_MASK; /* 12-bit field limit */ /* * Write value to LTC AADSIZE (rounded up to next 16 byte boundary) * plus the write value to LTC IV (rounded up to next 16 byte boundary) * plus the inputSize. If the result is less than max_ltc_fifo_sz * then all can be processed in one session FINALIZE. * Otherwise, we have to split into multiple session, going through UPDATE(s), INITIALIZE, UPDATE(s) and FINALIZE. */ single_ses_proc_all = (((aadSize + 15u) & 0xfffffff0u) + ((ivSize + 15u) & 0xfffffff0u) + inputSize) <= max_ltc_fifo_sz; /* setup key, algorithm and set the alg.state */ if (single_ses_proc_all) { ltc_symmetric_final(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeGCM, encryptMode); modeReg = base->MD; iv_only = (aadSize == 0) && (inputSize == 0); aad_only = (inputSize == 0); /* DS_MASK here is not a bug. IV size field can be written with more than 4-bits, * as the IVSZ write value, aligned to next 16 bytes boundary, is written also to the Data Size. * For example, I can write 22 to IVSZ, 32 will be written to Data Size and IVSZ will have value 6, which is 22 * mod 16. */ base->IVSZ = LTC_IVSZ_IL(iv_only) | ((ivSize)<C_DS_DS_MASK); ltc_move_to_ififo(base, iv, ivSize); if (iv_only && ivSize) { retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } } base->AADSZ = LTC_AADSZ_AL(aad_only) | ((aadSize)<C_DS_DS_MASK); ltc_move_to_ififo(base, aad, aadSize); if (aad_only && aadSize) { retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } } if (inputSize) { /* Workaround for the LTC Data Size register update errata TKT261180 */ while (16U < base->DS) { } ltc_symmetric_process_data(base, &src[0], inputSize, &dst[0]); } } else { ltc_symmetric_init(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeGCM, encryptMode); modeReg = base->MD; /* process IV */ if (ivSize) { /* last chunk of IV is always INITIALIZE (for GHASH to occur) */ retval = ltc_aes_gcm_process_iv_aad(base, iv, ivSize, modeReg, true, LTC_AES_GCM_TYPE_IV, kLTC_ModeInit); if (kStatus_Success != retval) { return retval; } } /* process AAD */ if (aadSize) { /* AS mode to process last chunk of AAD. it differs if we are in GMAC or GCM */ ltc_mode_t lastModeReg; if (0 == inputSize) { /* if there is no DATA, set mode to compute final MAC. this is GMAC mode */ lastModeReg = kLTC_ModeInitFinal; } else { /* there are confidential DATA. so process last chunk of AAD in UPDATE mode */ lastModeReg = kLTC_ModeUpdate; } retval = ltc_aes_gcm_process_iv_aad(base, aad, aadSize, modeReg, true, LTC_AES_GCM_TYPE_AAD, lastModeReg); if (kStatus_Success != retval) { return retval; } } /* there are DATA. */ if (inputSize) { /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= kLTC_ModeUpdate; base->MD = modeReg; retval = ltc_symmetric_process_data_multiple(base, &src[0], inputSize, &dst[0], modeReg, kLTC_ModeInitFinal); } } if (kStatus_Success != retval) { return retval; } retval = ltc_aes_process_tag(base, tag, tagSize, modeReg, LTC_GCM_TAG_IDX); return retval; } /******************************************************************************* * GCM Code public ******************************************************************************/ status_t LTC_AES_EncryptTagGcm(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, uint8_t *tag, uint32_t tagSize) { status_t status; status = ltc_aes_gcm_process(base, kLTC_ModeEncrypt, plaintext, size, iv, ivSize, aad, aadSize, key, keySize, ciphertext, tag, tagSize); ltc_clear_all(base, false); return status; } status_t LTC_AES_DecryptTagGcm(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, const uint8_t *tag, uint32_t tagSize) { uint8_t temp_tag[16] = {0}; /* max. octet length of Integrity Check Value ICV (tag) is 16 */ uint8_t *tag_ptr; status_t status; tag_ptr = NULL; if (tag) { ltc_memcpy(temp_tag, tag, tagSize); tag_ptr = &temp_tag[0]; } status = ltc_aes_gcm_process(base, kLTC_ModeDecrypt, ciphertext, size, iv, ivSize, aad, aadSize, key, keySize, plaintext, tag_ptr, tagSize); ltc_clear_all(base, false); return status; } #endif /* FSL_FEATURE_LTC_HAS_GCM */ /******************************************************************************* * CCM Code static ******************************************************************************/ static status_t ltc_aes_ccm_check_input_args(LTC_Type *base, const uint8_t *src, const uint8_t *iv, const uint8_t *key, uint8_t *dst, uint32_t ivSize, uint32_t aadSize, uint32_t keySize, uint32_t tagSize) { if (!base) { return kStatus_InvalidArgument; } /* tag can be NULL to skip tag processing */ if ((!src) || (!iv) || (!key) || (!dst)) { return kStatus_InvalidArgument; } /* size of Nonce (ivSize) must be element of 7,8,9,10,11,12,13 */ if ((ivSize < 7u) || (ivSize > 13u)) { return kStatus_InvalidArgument; } /* octet length of MAC (tagSize) must be element of 4,6,8,10,12,14,16 for tag processing or zero to skip tag * processing */ if (((tagSize > 0) && (tagSize < 4u)) || (tagSize > 16u) || (tagSize & 1u)) { return kStatus_InvalidArgument; } /* check if keySize is supported */ if (!ltc_check_key_size(keySize)) { return kStatus_InvalidArgument; } /* LTC does not support more AAD than this */ if (aadSize >= 65280u) { return kStatus_InvalidArgument; } return kStatus_Success; } static uint32_t swap_bytes(uint32_t in) { return (((in & 0x000000ffu) << 24) | ((in & 0x0000ff00u) << 8) | ((in & 0x00ff0000u) >> 8) | ((in & 0xff000000u) >> 24)); } static void ltc_aes_ccm_context_init( LTC_Type *base, uint32_t inputSize, const uint8_t *iv, uint32_t ivSize, uint32_t aadSize, uint32_t tagSize) { ltc_xcm_block_t blk; ltc_xcm_block_t blkZero = {{0x0u, 0x0u, 0x0u, 0x0u}}; int q; /* octet length of binary representation of the octet length of the payload. computed as (15 - n), where n is length of nonce(=ivSize) */ uint8_t flags; /* flags field in B0 and CTR0 */ /* compute B0 */ ltc_memcpy(&blk, &blkZero, sizeof(blk)); /* tagSize - size of output MAC */ q = 15 - ivSize; flags = (uint8_t)(8 * ((tagSize - 2) / 2) + q - 1); /* 8*M' + L' */ if (aadSize) { flags |= 0x40; /* Adata */ } blk.b[0] = flags; /* flags field */ blk.w[3] = swap_bytes(inputSize); /* message size, most significant byte first */ ltc_memcpy(&blk.b[1], iv, ivSize); /* nonce field */ /* Write B0 data to the context register. */ ltc_set_context(base, &blk.b[0], 16, 0); /* Write CTR0 to the context register. */ ltc_memcpy(&blk, &blkZero, sizeof(blk)); /* ctr(0) field = zero */ blk.b[0] = q - 1; /* flags field */ ltc_memcpy(&blk.b[1], iv, ivSize); /* nonce field */ ltc_set_context(base, &blk.b[0], 16, 4); } static status_t ltc_aes_ccm_process_aad( LTC_Type *base, uint32_t inputSize, const uint8_t *aad, uint32_t aadSize, ltc_mode_t *modeReg) { ltc_xcm_block_t blk = {{0x0u, 0x0u, 0x0u, 0x0u}}; uint32_t swapped; /* holds byte swap of uint32_t */ status_t retval; if (aadSize) { bool aad_only; bool aad_single_session; uint32_t sz = 0; aad_only = inputSize == 0u; aad_single_session = (((aadSize + 2u) + 15u) & 0xfffffff0u) <= LTC_FIFO_SZ_MAX_DOWN_ALGN; /* limit by CCM spec: 2^16 - 2^8 = 65280 */ /* encoding is two octets, msbyte first */ swapped = swap_bytes(aadSize); ltc_memcpy(&blk.b[0], ((uint8_t *)&swapped) + sizeof(uint16_t), sizeof(uint16_t)); sz = aadSize > 14u ? 14u : aadSize; /* limit aad to the end of 16 bytes blk */ ltc_memcpy(&blk.b[2], aad, sz); /* fill B1 with aad */ if (aad_single_session) { base->AADSZ = LTC_AADSZ_AL(aad_only) | ((aadSize + 2U) & LTC_DS_DS_MASK); /* move first AAD block (16 bytes block B1) to FIFO */ ltc_move_block_to_ififo(base, &blk, sizeof(blk)); } else { base->AADSZ = LTC_AADSZ_AL(true) | (16U); /* move first AAD block (16 bytes block B1) to FIFO */ ltc_move_block_to_ififo(base, &blk, sizeof(blk)); } /* track consumed AAD. sz bytes have been moved to fifo. */ aadSize -= sz; aad += sz; if (aad_single_session) { /* move remaining AAD to FIFO, then return, to continue with MDATA */ ltc_move_to_ififo(base, aad, aadSize); } else if (aadSize == 0u) { retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } } else { while (aadSize) { retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } *modeReg &= ~LTC_MD_AS_MASK; *modeReg |= (uint32_t)kLTC_ModeUpdate; base->MD = *modeReg; sz = LTC_FIFO_SZ_MAX_DOWN_ALGN; if (aadSize < sz) { base->AADSZ = LTC_AADSZ_AL(aad_only) | (aadSize & LTC_DS_DS_MASK); ltc_move_to_ififo(base, aad, aadSize); aadSize = 0; } else { base->AADSZ = LTC_AADSZ_AL(true) | (sz & LTC_DS_DS_MASK); ltc_move_to_ififo(base, aad, sz); aadSize -= sz; aad += sz; } } /* end while */ } /* end else */ } /* end if */ return kStatus_Success; } static status_t ltc_aes_ccm_process(LTC_Type *base, ltc_mode_encrypt_t encryptMode, const uint8_t *src, uint32_t inputSize, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, uint8_t *dst, uint8_t *tag, uint32_t tagSize) { status_t retval; /* return value */ uint32_t max_ltc_fifo_sz; /* maximum data size that we can put to LTC FIFO in one session. 12-bit limit. */ ltc_mode_t modeReg; /* read and write LTC mode register */ bool single_ses_proc_all; /* aad and src data can be processed in one session */ retval = ltc_aes_ccm_check_input_args(base, src, iv, key, dst, ivSize, aadSize, keySize, tagSize); /* API input validation */ if (kStatus_Success != retval) { return retval; } max_ltc_fifo_sz = LTC_DS_DS_MASK; /* 12-bit field limit */ /* Write value to LTC AADSIZE will be (aadSize+2) value. * The value will be rounded up to next 16 byte boundary and added to Data Size register. * We then add inputSize to Data Size register. If the resulting Data Size is less than max_ltc_fifo_sz * then all can be processed in one session INITIALIZE/FINALIZE. * Otherwise, we have to split into multiple session, going through INITIALIZE, UPDATE (if required) and FINALIZE. */ single_ses_proc_all = ((((aadSize + 2) + 15u) & 0xfffffff0u) + inputSize) <= max_ltc_fifo_sz; /* setup key, algorithm and set the alg.state to INITIALIZE */ if (single_ses_proc_all) { ltc_symmetric_init_final(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeCCM, encryptMode); } else { ltc_symmetric_init(base, key, keySize, kLTC_AlgorithmAES, kLTC_ModeCCM, encryptMode); } modeReg = base->MD; /* Initialize LTC context for AES CCM: block B0 and initial counter CTR0 */ ltc_aes_ccm_context_init(base, inputSize, iv, ivSize, aadSize, tagSize); /* Process additional authentication data, if there are any. * Need to split the job into individual sessions of up to 4096 bytes, due to LTC IFIFO data size limit. */ retval = ltc_aes_ccm_process_aad(base, inputSize, aad, aadSize, &modeReg); if (kStatus_Success != retval) { return retval; } /* Workaround for the LTC Data Size register update errata TKT261180 */ if (inputSize) { while (16u < base->DS) { } } /* Process message */ if (single_ses_proc_all) { retval = ltc_symmetric_process_data(base, &src[0], inputSize, &dst[0]); } else { retval = ltc_symmetric_process_data_multiple(base, &src[0], inputSize, &dst[0], modeReg, kLTC_ModeFinalize); } if (kStatus_Success != retval) { return retval; } retval = ltc_aes_process_tag(base, tag, tagSize, modeReg, LTC_CCM_TAG_IDX); return retval; } /******************************************************************************* * CCM Code public ******************************************************************************/ status_t LTC_AES_EncryptTagCcm(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, uint8_t *tag, uint32_t tagSize) { status_t status; status = ltc_aes_ccm_process(base, kLTC_ModeEncrypt, plaintext, size, iv, ivSize, aad, aadSize, key, keySize, ciphertext, tag, tagSize); ltc_clear_all(base, false); return status; } status_t LTC_AES_DecryptTagCcm(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t *iv, uint32_t ivSize, const uint8_t *aad, uint32_t aadSize, const uint8_t *key, uint32_t keySize, const uint8_t *tag, uint32_t tagSize) { uint8_t temp_tag[16] = {0}; /* max. octet length of MAC (tag) is 16 */ uint8_t *tag_ptr; status_t status; tag_ptr = NULL; if (tag) { ltc_memcpy(temp_tag, tag, tagSize); tag_ptr = &temp_tag[0]; } status = ltc_aes_ccm_process(base, kLTC_ModeDecrypt, ciphertext, size, iv, ivSize, aad, aadSize, key, keySize, plaintext, tag_ptr, tagSize); ltc_clear_all(base, false); return status; } #if defined(FSL_FEATURE_LTC_HAS_DES) && FSL_FEATURE_LTC_HAS_DES /******************************************************************************* * DES / 3DES Code static ******************************************************************************/ static status_t ltc_des_process(LTC_Type *base, const uint8_t *input, uint8_t *output, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE], ltc_mode_symmetric_alg_t modeAs, ltc_mode_encrypt_t modeEnc) { status_t retval; /* all but OFB, size must be 8-byte multiple */ if ((modeAs != kLTC_ModeOFB) && ((size < 8u) || (size % 8u))) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ ltc_symmetric_update(base, &key[0], LTC_DES_KEY_SIZE, kLTC_AlgorithmDES, modeAs, modeEnc); if ((modeAs != kLTC_ModeECB)) { ltc_set_context(base, iv, LTC_DES_IV_SIZE, 0); } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, input, size, output); ltc_clear_all(base, false); return retval; } status_t ltc_3des_check_input_args(ltc_mode_symmetric_alg_t modeAs, uint32_t size, const uint8_t *key1, const uint8_t *key2) { /* all but OFB, size must be 8-byte multiple */ if ((modeAs != kLTC_ModeOFB) && ((size < 8u) || (size % 8u))) { return kStatus_InvalidArgument; } if ((key1 == NULL) || (key2 == NULL)) { return kStatus_InvalidArgument; } return kStatus_Success; } static status_t ltc_3des_process(LTC_Type *base, const uint8_t *input, uint8_t *output, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE], ltc_mode_symmetric_alg_t modeAs, ltc_mode_encrypt_t modeEnc) { status_t retval; uint8_t key[LTC_DES_KEY_SIZE * 3]; uint8_t keySize = LTC_DES_KEY_SIZE * 2; retval = ltc_3des_check_input_args(modeAs, size, key1, key2); if (kStatus_Success != retval) { return retval; } ltc_memcpy(&key[0], &key1[0], LTC_DES_KEY_SIZE); ltc_memcpy(&key[LTC_DES_KEY_SIZE], &key2[0], LTC_DES_KEY_SIZE); if (key3) { ltc_memcpy(&key[LTC_DES_KEY_SIZE * 2], &key3[0], LTC_DES_KEY_SIZE); keySize = sizeof(key); } /* Initialize algorithm state. */ ltc_symmetric_update(base, &key[0], keySize, kLTC_Algorithm3DES, modeAs, modeEnc); if ((modeAs != kLTC_ModeECB)) { ltc_set_context(base, iv, LTC_DES_IV_SIZE, 0); } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, input, size, output); ltc_clear_all(base, false); return retval; } /******************************************************************************* * DES / 3DES Code public ******************************************************************************/ status_t LTC_DES_EncryptEcb( LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, plaintext, ciphertext, size, NULL, key, kLTC_ModeECB, kLTC_ModeEncrypt); } status_t LTC_DES_DecryptEcb( LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, ciphertext, plaintext, size, NULL, key, kLTC_ModeECB, kLTC_ModeDecrypt); } status_t LTC_DES_EncryptCbc(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, plaintext, ciphertext, size, iv, key, kLTC_ModeCBC, kLTC_ModeEncrypt); } status_t LTC_DES_DecryptCbc(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, ciphertext, plaintext, size, iv, key, kLTC_ModeCBC, kLTC_ModeDecrypt); } status_t LTC_DES_EncryptCfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, plaintext, ciphertext, size, iv, key, kLTC_ModeCFB, kLTC_ModeEncrypt); } status_t LTC_DES_DecryptCfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, ciphertext, plaintext, size, iv, key, kLTC_ModeCFB, kLTC_ModeDecrypt); } status_t LTC_DES_EncryptOfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, plaintext, ciphertext, size, iv, key, kLTC_ModeOFB, kLTC_ModeEncrypt); } status_t LTC_DES_DecryptOfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key[LTC_DES_KEY_SIZE]) { return ltc_des_process(base, ciphertext, plaintext, size, iv, key, kLTC_ModeOFB, kLTC_ModeDecrypt); } status_t LTC_DES2_EncryptEcb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, NULL, key1, key2, NULL, kLTC_ModeECB, kLTC_ModeEncrypt); } status_t LTC_DES3_EncryptEcb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, NULL, key1, key2, key3, kLTC_ModeECB, kLTC_ModeEncrypt); } status_t LTC_DES2_DecryptEcb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, NULL, key1, key2, NULL, kLTC_ModeECB, kLTC_ModeDecrypt); } status_t LTC_DES3_DecryptEcb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, NULL, key1, key2, key3, kLTC_ModeECB, kLTC_ModeDecrypt); } status_t LTC_DES2_EncryptCbc(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, NULL, kLTC_ModeCBC, kLTC_ModeEncrypt); } status_t LTC_DES3_EncryptCbc(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, key3, kLTC_ModeCBC, kLTC_ModeEncrypt); } status_t LTC_DES2_DecryptCbc(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, NULL, kLTC_ModeCBC, kLTC_ModeDecrypt); } status_t LTC_DES3_DecryptCbc(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, key3, kLTC_ModeCBC, kLTC_ModeDecrypt); } status_t LTC_DES2_EncryptCfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, NULL, kLTC_ModeCFB, kLTC_ModeEncrypt); } status_t LTC_DES3_EncryptCfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, key3, kLTC_ModeCFB, kLTC_ModeEncrypt); } status_t LTC_DES2_DecryptCfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, NULL, kLTC_ModeCFB, kLTC_ModeDecrypt); } status_t LTC_DES3_DecryptCfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, key3, kLTC_ModeCFB, kLTC_ModeDecrypt); } status_t LTC_DES2_EncryptOfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, NULL, kLTC_ModeOFB, kLTC_ModeEncrypt); } status_t LTC_DES3_EncryptOfb(LTC_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, plaintext, ciphertext, size, iv, key1, key2, key3, kLTC_ModeOFB, kLTC_ModeEncrypt); } status_t LTC_DES2_DecryptOfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, NULL, kLTC_ModeOFB, kLTC_ModeDecrypt); } status_t LTC_DES3_DecryptOfb(LTC_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, uint32_t size, const uint8_t iv[LTC_DES_IV_SIZE], const uint8_t key1[LTC_DES_KEY_SIZE], const uint8_t key2[LTC_DES_KEY_SIZE], const uint8_t key3[LTC_DES_KEY_SIZE]) { return ltc_3des_process(base, ciphertext, plaintext, size, iv, key1, key2, key3, kLTC_ModeOFB, kLTC_ModeDecrypt); } #endif /* FSL_FEATURE_LTC_HAS_DES */ /******************************************************************************* * HASH Definitions ******************************************************************************/ #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA #define LTC_SHA_BLOCK_SIZE 64 /*!< SHA-1, SHA-224 & SHA-256 block size */ #define LTC_HASH_BLOCK_SIZE LTC_SHA_BLOCK_SIZE /*!< LTC hash block size */ enum _ltc_sha_digest_len { kLTC_RunLenSha1 = 28u, kLTC_OutLenSha1 = 20u, kLTC_RunLenSha224 = 40u, kLTC_OutLenSha224 = 28u, kLTC_RunLenSha256 = 40u, kLTC_OutLenSha256 = 32u, }; #else #define LTC_HASH_BLOCK_SIZE LTC_AES_BLOCK_SIZE /*!< LTC hash block size */ #endif /* FSL_FEATURE_LTC_HAS_SHA */ /*! Internal states of the HASH creation process */ typedef enum _ltc_hash_algo_state { kLTC_HashInit = 1u, /*!< Key in the HASH context is the input key. */ kLTC_HashUpdate, /*!< HASH context has algorithm specific context: MAC, K2 and K3 (XCBC-MAC), MAC and L (CMAC), running digest (MDHA). Key in the HASH context is the derived key. */ } ltc_hash_algo_state_t; /*! 16/64-byte block represented as byte array or 4/16 32-bit words */ typedef union _ltc_hash_block { uint32_t w[LTC_HASH_BLOCK_SIZE / 4]; /*!< array of 32-bit words */ uint8_t b[LTC_HASH_BLOCK_SIZE]; /*!< byte array */ } ltc_hash_block_t; /*! Definitions of indexes into hash context array */ typedef enum _ltc_hash_ctx_indexes { kLTC_HashCtxKeyStartIdx = 12, /*!< context word array index where key is stored */ kLTC_HashCtxKeySize = 20, /*!< context word array index where key size is stored */ kLTC_HashCtxNumWords = 21, /*!< number of context array 32-bit words */ } ltc_hash_ctx_indexes; typedef struct _ltc_hash_ctx_internal { ltc_hash_block_t blk; /*!< memory buffer. only full 64/16-byte blocks are written to LTC during hash updates */ uint32_t blksz; /*!< number of valid bytes in memory buffer */ LTC_Type *base; /*!< LTC peripheral base address */ ltc_hash_algo_t algo; /*!< selected algorithm from the set of supported algorithms in ltc_drv_hash_algo */ ltc_hash_algo_state_t state; /*!< finite machine state of the hash software process */ uint32_t word[kLTC_HashCtxNumWords]; /*!< LTC module context that needs to be saved/restored between LTC jobs */ } ltc_hash_ctx_internal_t; /******************************************************************************* * HASH Code static ******************************************************************************/ static status_t ltc_hash_check_input_alg(ltc_hash_algo_t algo) { if ((algo != kLTC_XcbcMac) && (algo != kLTC_Cmac) #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA && (algo != kLTC_Sha1) && (algo != kLTC_Sha224) && (algo != kLTC_Sha256) #endif /* FSL_FEATURE_LTC_HAS_SHA */ ) { return kStatus_InvalidArgument; } return kStatus_Success; } static inline bool ltc_hash_alg_is_cmac(ltc_hash_algo_t algo) { return ((algo == kLTC_XcbcMac) || (algo == kLTC_Cmac)); } #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA static inline bool ltc_hash_alg_is_sha(ltc_hash_algo_t algo) { return ((algo == kLTC_Sha1) || (algo == kLTC_Sha224) || (algo == kLTC_Sha256)); } #endif /* FSL_FEATURE_LTC_HAS_SHA */ static status_t ltc_hash_check_input_args( LTC_Type *base, ltc_hash_ctx_t *ctx, ltc_hash_algo_t algo, const uint8_t *key, uint32_t keySize) { /* Check validity of input algorithm */ if (kStatus_Success != ltc_hash_check_input_alg(algo)) { return kStatus_InvalidArgument; } if ((NULL == ctx) || (NULL == base)) { return kStatus_InvalidArgument; } if (ltc_hash_alg_is_cmac(algo)) { if ((NULL == key) || (!ltc_check_key_size(keySize))) { return kStatus_InvalidArgument; } } return kStatus_Success; } static status_t ltc_hash_check_context(ltc_hash_ctx_internal_t *ctxInternal, const uint8_t *data) { if ((NULL == data) || (NULL == ctxInternal) || (NULL == ctxInternal->base) || (kStatus_Success != ltc_hash_check_input_alg(ctxInternal->algo))) { return kStatus_InvalidArgument; } return kStatus_Success; } static uint32_t ltc_hash_algo2mode(ltc_hash_algo_t algo, ltc_mode_algorithm_state_t asMode, uint32_t *algOutSize) { uint32_t modeReg = 0u; uint32_t outSize = 0u; /* Set LTC algorithm */ switch (algo) { case kLTC_XcbcMac: modeReg = (uint32_t)kLTC_AlgorithmAES | (uint32_t)kLTC_ModeXCBCMAC; outSize = 16u; break; case kLTC_Cmac: modeReg = (uint32_t)kLTC_AlgorithmAES | (uint32_t)kLTC_ModeCMAC; outSize = 16u; break; #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA case kLTC_Sha1: modeReg = (uint32_t)kLTC_AlgorithmSHA1; outSize = kLTC_OutLenSha1; break; case kLTC_Sha224: modeReg = (uint32_t)kLTC_AlgorithmSHA224; outSize = kLTC_OutLenSha224; break; case kLTC_Sha256: modeReg = (uint32_t)kLTC_AlgorithmSHA256; outSize = kLTC_OutLenSha256; break; #endif /* FSL_FEATURE_LTC_HAS_SHA */ default: break; } modeReg |= (uint32_t)asMode; if (algOutSize) { *algOutSize = outSize; } return modeReg; } static void ltc_hash_engine_init(ltc_hash_ctx_internal_t *ctx) { uint8_t *key; uint32_t keySize; LTC_Type *base; ltc_mode_symmetric_alg_t algo; base = ctx->base; #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA if (ltc_hash_alg_is_cmac(ctx->algo)) { #endif /* FSL_FEATURE_LTC_HAS_SHA */ /* * word[kLtcCmacCtxKeySize] = key_length * word[1-8] = key */ keySize = ctx->word[kLTC_HashCtxKeySize]; key = (uint8_t *)&ctx->word[kLTC_HashCtxKeyStartIdx]; /* set LTC mode register to INITIALIZE */ algo = (ctx->algo == kLTC_XcbcMac) ? kLTC_ModeXCBCMAC : kLTC_ModeCMAC; ltc_symmetric_init(base, key, keySize, kLTC_AlgorithmAES, algo, kLTC_ModeEncrypt); #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA } else if (ltc_hash_alg_is_sha(ctx->algo)) { /* Clear internal register states. */ base->CW = (uint32_t)kLTC_ClearAll; /* Set byte swap on for several registers we will be reading and writing * user data to/from. */ base->CTL |= kLTC_CtrlSwapAll; } else { /* do nothing in this case */ } #endif /* FSL_FEATURE_LTC_HAS_SHA */ } static void ltc_hash_save_context(ltc_hash_ctx_internal_t *ctx) { uint32_t sz; LTC_Type *base; base = ctx->base; /* Get context size */ switch (ctx->algo) { case kLTC_XcbcMac: /* * word[0-3] = mac * word[3-7] = k3 * word[8-11] = k2 * word[kLtcCmacCtxKeySize] = keySize */ sz = 12 * sizeof(uint32_t); break; case kLTC_Cmac: /* * word[0-3] = mac * word[3-7] = L */ sz = 8 * sizeof(uint32_t); break; #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA case kLTC_Sha1: sz = (kLTC_RunLenSha1); break; case kLTC_Sha224: sz = (kLTC_RunLenSha224); break; case kLTC_Sha256: sz = (kLTC_RunLenSha256); break; #endif /* FSL_FEATURE_LTC_HAS_SHA */ default: sz = 0; break; } ltc_get_context(base, (uint8_t *)&ctx->word[0], sz, 0); if (true == ltc_hash_alg_is_cmac(ctx->algo)) { /* word[12-19] = key */ ltc_get_key(base, (uint8_t *)&ctx->word[kLTC_HashCtxKeyStartIdx], ctx->word[kLTC_HashCtxKeySize]); } } static void ltc_hash_restore_context(ltc_hash_ctx_internal_t *ctx) { uint32_t sz; uint32_t keySize; LTC_Type *base; base = ctx->base; /* Get context size */ switch (ctx->algo) { case kLTC_XcbcMac: /* * word[0-3] = mac * word[3-7] = k3 * word[8-11] = k2 * word[kLtcCmacCtxKeySize] = keySize */ sz = 12 * sizeof(uint32_t); break; case kLTC_Cmac: /* * word[0-3] = mac * word[3-7] = L */ sz = 8 * sizeof(uint32_t); break; #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA case kLTC_Sha1: sz = (kLTC_RunLenSha1); break; case kLTC_Sha224: sz = (kLTC_RunLenSha224); break; case kLTC_Sha256: sz = (kLTC_RunLenSha256); break; #endif /* FSL_FEATURE_LTC_HAS_SHA */ default: sz = 0; break; } ltc_set_context(base, (const uint8_t *)&ctx->word[0], sz, 0); if (ltc_hash_alg_is_cmac(ctx->algo)) { /* * word[12-19] = key * word[kLtcCmacCtxKeySize] = keySize */ base->CW = kLTC_ClearKey; /* clear Key and Key Size registers */ keySize = ctx->word[kLTC_HashCtxKeySize]; /* Write the key in place. */ ltc_set_key(base, (const uint8_t *)&ctx->word[kLTC_HashCtxKeyStartIdx], keySize); /* Write the key size. This must be done after writing the key, and this * action locks the ability to modify the key registers. */ base->KS = keySize; } } static void ltc_hash_prepare_context_switch(LTC_Type *base) { base->CW = (uint32_t)kLTC_ClearDataSize | (uint32_t)kLTC_ClearMode; base->STA = kLTC_StatusDoneIsr; } static uint32_t ltc_hash_get_block_size(ltc_hash_algo_t algo) { if ((algo == kLTC_XcbcMac) || (algo == kLTC_Cmac)) { return (uint32_t)LTC_AES_BLOCK_SIZE; } #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA else if ((algo == kLTC_Sha1) || (algo == kLTC_Sha224) || (algo == kLTC_Sha256)) { return (uint32_t)LTC_SHA_BLOCK_SIZE; } else { return 0; } #else return 0; #endif } static void ltc_hash_block_to_ififo(LTC_Type *base, const ltc_hash_block_t *blk, uint32_t numBytes, uint32_t blockSize) { uint32_t i = 0; uint32_t words; words = numBytes / 4u; if (numBytes % 4u) { words++; } if (words > blockSize / 4u) { words = blockSize / 4u; } while (i < words) { if (0U == (base->FIFOSTA & LTC_FIFOSTA_IFF_MASK)) { /* Copy data to the input FIFO. */ base->IFIFO = blk->w[i++]; } } } static void ltc_hash_move_to_ififo(ltc_hash_ctx_internal_t *ctx, const uint8_t *data, uint32_t dataSize, uint32_t blockSize) { ltc_hash_block_t blkZero; uint32_t i; for (i = 0; i < ARRAY_SIZE(blkZero.w); i++) { blkZero.w[i] = 0; } while (dataSize) { if (dataSize >= blockSize) { ltc_memcpy(&ctx->blk, data, blockSize); ltc_hash_block_to_ififo(ctx->base, &ctx->blk, blockSize, blockSize); dataSize -= blockSize; data += blockSize; } else { /* last incomplete 16/64-bytes block of this message chunk */ ltc_memcpy(&ctx->blk, &blkZero, sizeof(ctx->blk)); ltc_memcpy(&ctx->blk, data, dataSize); ctx->blksz = dataSize; dataSize = 0; } } } static status_t ltc_hash_merge_and_flush_buf(ltc_hash_ctx_internal_t *ctx, const uint8_t *input, uint32_t inputSize, ltc_mode_t modeReg, uint32_t blockSize, uint32_t *consumedSize) { uint32_t sz; LTC_Type *base; status_t status = kStatus_Success; base = ctx->base; sz = 0; if (ctx->blksz) { sz = blockSize - ctx->blksz; if (sz > inputSize) { sz = inputSize; } ltc_memcpy(ctx->blk.b + ctx->blksz, input, sz); input += sz; inputSize -= sz; ctx->blksz += sz; if (ctx->blksz == blockSize) { base->DS = blockSize; ltc_hash_block_to_ififo(base, &ctx->blk, blockSize, blockSize); ctx->blksz = 0; status = ltc_wait(base); if (kStatus_Success != status) { return status; } /* if there is still inputSize left, make sure LTC alg.state is set to UPDATE and continue */ if (inputSize) { /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= kLTC_ModeUpdate; base->MD = modeReg; } } } if (consumedSize) { *consumedSize = sz; } return status; } static status_t ltc_hash_move_rest_to_context( ltc_hash_ctx_internal_t *ctx, const uint8_t *data, uint32_t dataSize, ltc_mode_t modeReg, uint32_t blockSize) { status_t status = kStatus_Success; ltc_hash_block_t blkZero; uint32_t i; /* make blkZero clear */ for (i = 0; i < ARRAY_SIZE(blkZero.w); i++) { blkZero.w[i] = 0; } while (dataSize) { if (dataSize > blockSize) { dataSize -= blockSize; data += blockSize; } else { if (dataSize + ctx->blksz > blockSize) { uint32_t sz = 0; status = ltc_hash_merge_and_flush_buf(ctx, data, dataSize, modeReg, blockSize, &sz); if (kStatus_Success != status) { return status; } data += sz; dataSize -= sz; } /* last incomplete 16/64-bytes block of this message chunk */ ltc_memcpy(&ctx->blk, &blkZero, blockSize); ltc_memcpy(&ctx->blk, data, dataSize); ctx->blksz = dataSize; dataSize = 0; } } return status; } static status_t ltc_hash_process_input_data(ltc_hash_ctx_internal_t *ctx, const uint8_t *input, uint32_t inputSize, ltc_mode_t modeReg) { uint32_t sz = 0; LTC_Type *base; uint32_t blockSize = 0; status_t status = kStatus_Success; blockSize = ltc_hash_get_block_size(ctx->algo); if (blockSize == 0) { return kStatus_Fail; } base = ctx->base; /* fill context struct blk and flush to LTC ififo in case it is full block */ status = ltc_hash_merge_and_flush_buf(ctx, input, inputSize, modeReg, blockSize, &sz); if (kStatus_Success != status) { return status; } input += sz; inputSize -= sz; /* if there is still more than or equal to 64 bytes, move each 64 bytes through LTC */ sz = LTC_DS_DS_MASK + 1u - LTC_HASH_BLOCK_SIZE; while (inputSize) { if (inputSize < sz) { uint32_t lastSize; lastSize = inputSize % blockSize; if (lastSize == 0) { lastSize = blockSize; } inputSize -= lastSize; if (inputSize) { /* move all complete blocks to ififo. */ base->DS = inputSize; ltc_hash_move_to_ififo(ctx, input, inputSize, blockSize); status = ltc_wait(base); if (kStatus_Success != status) { return status; } input += inputSize; } /* keep last (in)complete 16-bytes block in context struct. */ /* when 3rd argument of cmac_move_to_ififo() is <= 16 bytes, it only stores the data to context struct */ status = ltc_hash_move_rest_to_context(ctx, input, lastSize, modeReg, blockSize); if (kStatus_Success != status) { return status; } inputSize = 0; } else { base->DS = sz; ltc_hash_move_to_ififo(ctx, input, sz, blockSize); inputSize -= sz; input += sz; status = ltc_wait(base); if (kStatus_Success != status) { return status; } /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= kLTC_ModeUpdate; base->MD = modeReg; } } /* end while */ return status; } /******************************************************************************* * HASH Code public ******************************************************************************/ status_t LTC_HASH_Init(LTC_Type *base, ltc_hash_ctx_t *ctx, ltc_hash_algo_t algo, const uint8_t *key, uint32_t keySize) { status_t ret; ltc_hash_ctx_internal_t *ctxInternal; uint32_t i; ret = ltc_hash_check_input_args(base, ctx, algo, key, keySize); if (ret != kStatus_Success) { return ret; } /* set algorithm in context struct for later use */ ctxInternal = (ltc_hash_ctx_internal_t *)ctx; ctxInternal->algo = algo; for (i = 0; i < kLTC_HashCtxNumWords; i++) { ctxInternal->word[i] = 0u; } /* Steps required only using AES engine */ if (ltc_hash_alg_is_cmac(algo)) { /* store input key and key length in context struct for later use */ ctxInternal->word[kLTC_HashCtxKeySize] = keySize; ltc_memcpy(&ctxInternal->word[kLTC_HashCtxKeyStartIdx], key, keySize); } ctxInternal->blksz = 0u; for (i = 0; i < sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); i++) { ctxInternal->blk.w[0] = 0u; } ctxInternal->state = kLTC_HashInit; ctxInternal->base = base; return kStatus_Success; } status_t LTC_HASH_Update(ltc_hash_ctx_t *ctx, const uint8_t *input, uint32_t inputSize) { bool isUpdateState; ltc_mode_t modeReg = 0; /* read and write LTC mode register */ LTC_Type *base; status_t status; ltc_hash_ctx_internal_t *ctxInternal; uint32_t blockSize; ctxInternal = (ltc_hash_ctx_internal_t *)ctx; status = ltc_hash_check_context(ctxInternal, input); if (kStatus_Success != status) { return status; } base = ctxInternal->base; blockSize = ltc_hash_get_block_size(ctxInternal->algo); /* if we are still less than 64 bytes, keep only in context */ if ((ctxInternal->blksz + inputSize) <= blockSize) { ltc_memcpy((&ctxInternal->blk.b[0]) + ctxInternal->blksz, input, inputSize); ctxInternal->blksz += inputSize; return status; } else { isUpdateState = ctxInternal->state == kLTC_HashUpdate; if (ctxInternal->state == kLTC_HashInit) { /* set LTC mode register to INITIALIZE job */ ltc_hash_engine_init(ctxInternal); #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA if (ltc_hash_alg_is_cmac(ctxInternal->algo)) { #endif /* FSL_FEATURE_LTC_HAS_SHA */ ctxInternal->state = kLTC_HashUpdate; isUpdateState = true; base->DS = 0u; status = ltc_wait(base); #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA } else { /* Set the proper block and algorithm mode. */ modeReg = ltc_hash_algo2mode(ctxInternal->algo, kLTC_ModeInit, NULL); base->MD = modeReg; ctxInternal->state = kLTC_HashUpdate; status = ltc_hash_process_input_data(ctxInternal, input, inputSize, modeReg); ltc_hash_save_context(ctxInternal); } #endif /* FSL_FEATURE_LTC_HAS_SHA */ } else if (isUpdateState) { /* restore LTC context from context struct */ ltc_hash_restore_context(ctxInternal); } else { /* nothing special at this place */ } } if (kStatus_Success != status) { return status; } if (isUpdateState) { /* set LTC mode register to UPDATE job */ ltc_hash_prepare_context_switch(base); base->CW = kLTC_ClearDataSize; modeReg = ltc_hash_algo2mode(ctxInternal->algo, kLTC_ModeUpdate, NULL); base->MD = modeReg; /* process input data and save LTC context to context structure */ status = ltc_hash_process_input_data(ctxInternal, input, inputSize, modeReg); ltc_hash_save_context(ctxInternal); } ltc_clear_all(base, false); return status; } status_t LTC_HASH_Finish(ltc_hash_ctx_t *ctx, uint8_t *output, uint32_t *outputSize) { ltc_mode_t modeReg; /* read and write LTC mode register */ LTC_Type *base; uint32_t algOutSize = 0; status_t status; ltc_hash_ctx_internal_t *ctxInternal; uint32_t *ctxW; uint32_t i; ctxInternal = (ltc_hash_ctx_internal_t *)ctx; status = ltc_hash_check_context(ctxInternal, output); if (kStatus_Success != status) { return status; } base = ctxInternal->base; ltc_hash_prepare_context_switch(base); base->CW = kLTC_ClearDataSize; if (ctxInternal->state == kLTC_HashInit) { ltc_hash_engine_init(ctxInternal); #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA if (ltc_hash_alg_is_cmac(ctxInternal->algo)) { #endif /* FSL_FEATURE_LTC_HAS_SHA */ base->DS = 0u; status = ltc_wait(base); if (kStatus_Success != status) { return status; } modeReg = ltc_hash_algo2mode(ctxInternal->algo, kLTC_ModeFinalize, &algOutSize); #if defined(FSL_FEATURE_LTC_HAS_SHA) && FSL_FEATURE_LTC_HAS_SHA } else { modeReg = ltc_hash_algo2mode(ctxInternal->algo, kLTC_ModeInitFinal, &algOutSize); } #endif /* FSL_FEATURE_LTC_HAS_SHA */ base->MD = modeReg; } else { modeReg = ltc_hash_algo2mode(ctxInternal->algo, kLTC_ModeFinalize, &algOutSize); base->MD = modeReg; /* restore LTC context from context struct */ ltc_hash_restore_context(ctxInternal); } /* flush message last incomplete block, if there is any, or write zero to data size register. */ base->DS = ctxInternal->blksz; ltc_hash_block_to_ififo(base, &ctxInternal->blk, ctxInternal->blksz, ltc_hash_get_block_size(ctxInternal->algo)); /* Wait for finish of the encryption */ status = ltc_wait(base); if (outputSize) { if (algOutSize < *outputSize) { *outputSize = algOutSize; } else { algOutSize = *outputSize; } } ltc_get_context(base, &output[0], algOutSize, 0u); ctxW = (uint32_t *)ctx; for (i = 0; i < LTC_HASH_CTX_SIZE; i++) { ctxW[i] = 0u; } ltc_clear_all(base, false); return status; } status_t LTC_HASH(LTC_Type *base, ltc_hash_algo_t algo, const uint8_t *input, uint32_t inputSize, const uint8_t *key, uint32_t keySize, uint8_t *output, uint32_t *outputSize) { status_t status; ltc_hash_ctx_t ctx; status = LTC_HASH_Init(base, &ctx, algo, key, keySize); if (status != kStatus_Success) { return status; } status = LTC_HASH_Update(&ctx, input, inputSize); if (status != kStatus_Success) { return status; } status = LTC_HASH_Finish(&ctx, output, outputSize); return status; } /******************************************************************************* * PKHA Code static ******************************************************************************/ #if defined(FSL_FEATURE_LTC_HAS_PKHA) && FSL_FEATURE_LTC_HAS_PKHA static status_t ltc_pkha_clear_regabne(LTC_Type *base, bool A, bool B, bool N, bool E) { ltc_mode_t mode; /* Set the PKHA algorithm and the appropriate function. */ mode = (uint32_t)kLTC_AlgorithmPKHA | 1U; /* Set ram area to clear. Clear all. */ if (A) { mode |= 1U << 19U; } if (B) { mode |= 1U << 18U; } if (N) { mode |= 1U << 16U; } if (E) { mode |= 1U << 17U; } /* Write the mode register to the hardware. * NOTE: This will begin the operation. */ base->MDPK = mode; /* Wait for 'done' */ return ltc_wait(base); } static void ltc_pkha_default_parms(ltc_pkha_mode_params_t *params) { params->func = (ltc_pkha_func_t)0; params->arithType = kLTC_PKHA_IntegerArith; params->montFormIn = kLTC_PKHA_NormalValue; params->montFormOut = kLTC_PKHA_NormalValue; params->srcReg = kLTC_PKHA_RegAll; params->srcQuad = kLTC_PKHA_Quad0; params->dstReg = kLTC_PKHA_RegAll; params->dstQuad = kLTC_PKHA_Quad0; params->equalTime = kLTC_PKHA_NoTimingEqualized; params->r2modn = kLTC_PKHA_CalcR2; } static void ltc_pkha_write_word(LTC_Type *base, ltc_pkha_reg_area_t reg, uint8_t index, uint32_t data) { switch (reg) { case kLTC_PKHA_RegA: base->PKA[index] = data; break; case kLTC_PKHA_RegB: base->PKB[index] = data; break; case kLTC_PKHA_RegN: base->PKN[index] = data; break; case kLTC_PKHA_RegE: base->PKE[index] = data; break; default: break; } } static uint32_t ltc_pkha_read_word(LTC_Type *base, ltc_pkha_reg_area_t reg, uint8_t index) { uint32_t retval; switch (reg) { case kLTC_PKHA_RegA: retval = base->PKA[index]; break; case kLTC_PKHA_RegB: retval = base->PKB[index]; break; case kLTC_PKHA_RegN: retval = base->PKN[index]; break; case kLTC_PKHA_RegE: retval = base->PKE[index]; break; default: retval = 0; break; } return retval; } static status_t ltc_pkha_write_reg( LTC_Type *base, ltc_pkha_reg_area_t reg, uint8_t quad, const uint8_t *data, uint16_t dataSize) { /* Select the word-based start index for each quadrant of 64 bytes. */ uint8_t startIndex = (quad * 16u); uint32_t outWord; while (dataSize > 0) { if (dataSize >= sizeof(uint32_t)) { ltc_pkha_write_word(base, reg, startIndex++, ltc_get_word_from_unaligned(data)); dataSize -= sizeof(uint32_t); data += sizeof(uint32_t); } else /* (dataSize > 0) && (dataSize < 4) */ { outWord = 0; ltc_memcpy(&outWord, data, dataSize); ltc_pkha_write_word(base, reg, startIndex, outWord); dataSize = 0; } } return kStatus_Success; } static void ltc_pkha_read_reg(LTC_Type *base, ltc_pkha_reg_area_t reg, uint8_t quad, uint8_t *data, uint16_t dataSize) { /* Select the word-based start index for each quadrant of 64 bytes. */ uint8_t startIndex = (quad * 16u); uint16_t calcSize; uint32_t word; while (dataSize > 0) { word = ltc_pkha_read_word(base, reg, startIndex++); calcSize = (dataSize >= sizeof(uint32_t)) ? sizeof(uint32_t) : dataSize; ltc_memcpy(data, &word, calcSize); data += calcSize; dataSize -= calcSize; } } static void ltc_pkha_init_data(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, const uint8_t *E, uint16_t sizeE) { uint32_t clearMask = kLTC_ClearMode; /* clear Mode Register */ /* Clear internal register states. */ if (sizeA) { clearMask |= kLTC_ClearPkhaSizeA; } if (sizeB) { clearMask |= kLTC_ClearPkhaSizeB; } if (sizeN) { clearMask |= kLTC_ClearPkhaSizeN; } if (sizeE) { clearMask |= kLTC_ClearPkhaSizeE; } base->CW = clearMask; base->STA = kLTC_StatusDoneIsr; ltc_pkha_clear_regabne(base, A, B, N, E); /* Write register sizes. */ /* Write modulus (N) and A and B register arguments. */ if (sizeN) { base->PKNSZ = sizeN; if (N) { ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, sizeN); } } if (sizeA) { base->PKASZ = sizeA; if (A) { ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A, sizeA); } } if (sizeB) { base->PKBSZ = sizeB; if (B) { ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, B, sizeB); } } if (sizeE) { base->PKESZ = sizeE; if (E) { ltc_pkha_write_reg(base, kLTC_PKHA_RegE, 0, E, sizeE); } } } static void ltc_pkha_mode_set_src_reg_copy(ltc_mode_t *outMode, ltc_pkha_reg_area_t reg) { int i = 0; do { reg = (ltc_pkha_reg_area_t)(((uint32_t)reg) >> 1u); i++; } while (reg); i = 4 - i; /* Source register must not be E. */ if (i != 2) { *outMode |= ((uint32_t)i << 17u); } } static void ltc_pkha_mode_set_dst_reg_copy(ltc_mode_t *outMode, ltc_pkha_reg_area_t reg) { int i = 0; do { reg = (ltc_pkha_reg_area_t)(((uint32_t)reg) >> 1u); i++; } while (reg); i = 4 - i; *outMode |= ((uint32_t)i << 10u); } static void ltc_pkha_mode_set_src_seg_copy(ltc_mode_t *outMode, const ltc_pkha_quad_area_t quad) { *outMode |= ((uint32_t)quad << 8u); } static void ltc_pkha_mode_set_dst_seg_copy(ltc_mode_t *outMode, const ltc_pkha_quad_area_t quad) { *outMode |= ((uint32_t)quad << 6u); } /*! * @brief Starts the PKHA operation. * * This function starts an operation configured by the params parameter. * * @param base LTC peripheral base address * @param params Configuration structure containing all settings required for PKHA operation. */ static status_t ltc_pkha_init_mode(LTC_Type *base, const ltc_pkha_mode_params_t *params) { ltc_mode_t modeReg; status_t retval; /* Set the PKHA algorithm and the appropriate function. */ modeReg = kLTC_AlgorithmPKHA; modeReg |= (uint32_t)params->func; if ((params->func == kLTC_PKHA_CopyMemSizeN) || (params->func == kLTC_PKHA_CopyMemSizeSrc)) { /* Set source and destination registers and quads. */ ltc_pkha_mode_set_src_reg_copy(&modeReg, params->srcReg); ltc_pkha_mode_set_dst_reg_copy(&modeReg, params->dstReg); ltc_pkha_mode_set_src_seg_copy(&modeReg, params->srcQuad); ltc_pkha_mode_set_dst_seg_copy(&modeReg, params->dstQuad); } else { /* Set the arithmetic type - integer or binary polynomial (F2m). */ modeReg |= ((uint32_t)params->arithType << 17u); /* Set to use Montgomery form of inputs and/or outputs. */ modeReg |= ((uint32_t)params->montFormIn << 19u); modeReg |= ((uint32_t)params->montFormOut << 18u); /* Set to use pre-computed R2modN */ modeReg |= ((uint32_t)params->r2modn << 16u); } modeReg |= ((uint32_t)params->equalTime << 10u); /* Write the mode register to the hardware. * NOTE: This will begin the operation. */ base->MDPK = modeReg; retval = ltc_wait(base); return (retval); } static status_t ltc_pkha_modR2( LTC_Type *base, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { status_t status; ltc_pkha_mode_params_t params; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModR2; params.arithType = arithType; ltc_pkha_init_data(base, NULL, 0, NULL, 0, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } return status; } static status_t ltc_pkha_modmul(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType, ltc_pkha_montgomery_form_t montIn, ltc_pkha_montgomery_form_t montOut, ltc_pkha_timing_t equalTime) { ltc_pkha_mode_params_t params; status_t status; if (arithType == kLTC_PKHA_IntegerArith) { if (LTC_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } if (LTC_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } } ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModMul; params.arithType = arithType; params.montFormIn = montIn; params.montFormOut = montOut; params.equalTime = equalTime; ltc_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } return status; } /******************************************************************************* * PKHA Code public ******************************************************************************/ int LTC_PKHA_CompareBigNum(const uint8_t *a, size_t sizeA, const uint8_t *b, size_t sizeB) { int retval = 0; /* skip zero msbytes - integer a */ while ((sizeA) && (0u == a[sizeA - 1])) { sizeA--; } /* skip zero msbytes - integer b */ while ((sizeB) && (0u == b[sizeB - 1])) { sizeB--; } if (sizeA > sizeB) { retval = 1; } /* int a has more non-zero bytes, thus it is bigger than b */ else if (sizeA < sizeB) { retval = -1; } /* int b has more non-zero bytes, thus it is bigger than a */ else if (sizeA == 0) { retval = 0; } /* sizeA = sizeB = 0 */ else { int n; int i; int val; uint32_t equal; n = sizeA - 1; i = 0; equal = 0; while (n >= 0) { uint32_t chXor = a[i] ^ b[i]; equal |= chXor; val = (int)chXor * (a[i] - b[i]); if (val < 0) { *(volatile int*)&retval = -1; } if (val > 0) { *(volatile int*)&retval = 1; } if (val == 0) { *(volatile int*)&val = 1; } i++; n--; } if (0 == equal) { retval = 0; } } return (retval); } status_t LTC_PKHA_NormalToMontgomery(LTC_Type *base, const uint8_t *N, uint16_t sizeN, uint8_t *A, uint16_t *sizeA, uint8_t *B, uint16_t *sizeB, uint8_t *R2, uint16_t *sizeR2, ltc_pkha_timing_t equalTime, ltc_pkha_f2m_t arithType) { status_t status; /* need to convert our Integer inputs into Montgomery format */ if (N && sizeN && R2 && sizeR2) { /* 1. R2 = MOD_R2(N) */ status = ltc_pkha_modR2(base, N, sizeN, R2, sizeR2, arithType); if (status != kStatus_Success) { return status; } /* 2. A(Montgomery) = MOD_MUL_IM_OM(A, R2, N) */ if (A && sizeA) { status = ltc_pkha_modmul(base, A, *sizeA, R2, *sizeR2, N, sizeN, A, sizeA, arithType, kLTC_PKHA_MontgomeryFormat, kLTC_PKHA_MontgomeryFormat, equalTime); if (status != kStatus_Success) { return status; } } /* 2. B(Montgomery) = MOD_MUL_IM_OM(B, R2, N) */ if (B && sizeB) { status = ltc_pkha_modmul(base, B, *sizeB, R2, *sizeR2, N, sizeN, B, sizeB, arithType, kLTC_PKHA_MontgomeryFormat, kLTC_PKHA_MontgomeryFormat, equalTime); if (status != kStatus_Success) { return status; } } ltc_clear_all(base, true); } else { status = kStatus_InvalidArgument; } return status; } status_t LTC_PKHA_MontgomeryToNormal(LTC_Type *base, const uint8_t *N, uint16_t sizeN, uint8_t *A, uint16_t *sizeA, uint8_t *B, uint16_t *sizeB, ltc_pkha_timing_t equalTime, ltc_pkha_f2m_t arithType) { uint8_t one = 1; status_t status = kStatus_InvalidArgument; /* A = MOD_MUL_IM_OM(A(Montgomery), 1, N) */ if (A && sizeA) { status = ltc_pkha_modmul(base, A, *sizeA, &one, sizeof(one), N, sizeN, A, sizeA, arithType, kLTC_PKHA_MontgomeryFormat, kLTC_PKHA_MontgomeryFormat, equalTime); if (kStatus_Success != status) { return status; } } /* B = MOD_MUL_IM_OM(B(Montgomery), 1, N) */ if (B && sizeB) { status = ltc_pkha_modmul(base, B, *sizeB, &one, sizeof(one), N, sizeN, B, sizeB, arithType, kLTC_PKHA_MontgomeryFormat, kLTC_PKHA_MontgomeryFormat, equalTime); if (kStatus_Success != status) { return status; } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModAdd(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { ltc_pkha_mode_params_t params; status_t status; if (arithType == kLTC_PKHA_IntegerArith) { if (LTC_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } if (LTC_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } } ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModAdd; params.arithType = arithType; ltc_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModSub1(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize) { ltc_pkha_mode_params_t params; status_t status; if (LTC_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } if (LTC_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModSub1; ltc_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModSub2(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize) { ltc_pkha_mode_params_t params; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModSub2; ltc_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModMul(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType, ltc_pkha_montgomery_form_t montIn, ltc_pkha_montgomery_form_t montOut, ltc_pkha_timing_t equalTime) { status_t status; status = ltc_pkha_modmul(base, A, sizeA, B, sizeB, N, sizeN, result, resultSize, arithType, montIn, montOut, equalTime); ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModExp(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *N, uint16_t sizeN, const uint8_t *E, uint16_t sizeE, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType, ltc_pkha_montgomery_form_t montIn, ltc_pkha_timing_t equalTime) { ltc_pkha_mode_params_t params; status_t status; if (arithType == kLTC_PKHA_IntegerArith) { if (LTC_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } } ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModExp; params.arithType = arithType; params.montFormIn = montIn; params.equalTime = equalTime; ltc_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, E, sizeE); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModRed(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { ltc_pkha_mode_params_t params; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModRed; params.arithType = arithType; ltc_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModInv(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { ltc_pkha_mode_params_t params; status_t status; /* A must be less than N -> LTC_PKHA_CompareBigNum() must return -1 */ if (arithType == kLTC_PKHA_IntegerArith) { if (LTC_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0) { return (kStatus_InvalidArgument); } } ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithModInv; params.arithType = arithType; ltc_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ModR2( LTC_Type *base, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { status_t status; status = ltc_pkha_modR2(base, N, sizeN, result, resultSize, arithType); ltc_clear_all(base, true); return status; } status_t LTC_PKHA_GCD(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *N, uint16_t sizeN, uint8_t *result, uint16_t *resultSize, ltc_pkha_f2m_t arithType) { ltc_pkha_mode_params_t params; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithGcd; params.arithType = arithType; ltc_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the result and size from register B0. */ if (resultSize && result) { *resultSize = base->PKBSZ; /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, result, *resultSize); } } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_PrimalityTest(LTC_Type *base, const uint8_t *A, uint16_t sizeA, const uint8_t *B, uint16_t sizeB, const uint8_t *N, uint16_t sizeN, bool *res) { uint8_t result; ltc_pkha_mode_params_t params; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithPrimalityTest; ltc_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 0, &result, 1); *res = (bool)result; } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ECC_PointAdd(LTC_Type *base, const ltc_pkha_ecc_point_t *A, const ltc_pkha_ecc_point_t *B, const uint8_t *N, const uint8_t *R2modN, const uint8_t *aCurveParam, const uint8_t *bCurveParam, uint8_t size, ltc_pkha_f2m_t arithType, ltc_pkha_ecc_point_t *result) { ltc_pkha_mode_params_t params; uint32_t clearMask; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithEccAdd; params.arithType = arithType; params.r2modn = R2modN ? kLTC_PKHA_InputR2 : kLTC_PKHA_CalcR2; clearMask = kLTC_ClearMode; /* Clear internal register states. */ clearMask |= kLTC_ClearPkhaSizeA; clearMask |= kLTC_ClearPkhaSizeB; clearMask |= kLTC_ClearPkhaSizeN; clearMask |= kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = kLTC_StatusDoneIsr; ltc_pkha_clear_regabne(base, true, true, true, false); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKASZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A->X, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 1, A->Y, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 1, B->X, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 2, B->Y, size); if (R2modN) { ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 3, R2modN, size); } status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 1, result->X, size); ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 2, result->Y, size); } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ECC_PointDouble(LTC_Type *base, const ltc_pkha_ecc_point_t *B, const uint8_t *N, const uint8_t *aCurveParam, const uint8_t *bCurveParam, uint8_t size, ltc_pkha_f2m_t arithType, ltc_pkha_ecc_point_t *result) { ltc_pkha_mode_params_t params; uint32_t clearMask; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithEccDouble; params.arithType = arithType; clearMask = kLTC_ClearMode; /* Clear internal register states. */ clearMask |= kLTC_ClearPkhaSizeA; clearMask |= kLTC_ClearPkhaSizeB; clearMask |= kLTC_ClearPkhaSizeN; clearMask |= kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = kLTC_StatusDoneIsr; ltc_pkha_clear_regabne(base, true, true, true, false); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKASZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 1, B->X, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 2, B->Y, size); status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 1, result->X, size); ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 2, result->Y, size); } ltc_clear_all(base, true); return status; } status_t LTC_PKHA_ECC_PointMul(LTC_Type *base, const ltc_pkha_ecc_point_t *A, const uint8_t *E, uint8_t sizeE, const uint8_t *N, const uint8_t *R2modN, const uint8_t *aCurveParam, const uint8_t *bCurveParam, uint8_t size, ltc_pkha_timing_t equalTime, ltc_pkha_f2m_t arithType, ltc_pkha_ecc_point_t *result, bool *infinity) { ltc_pkha_mode_params_t params; uint32_t clearMask; status_t status; ltc_pkha_default_parms(¶ms); params.func = kLTC_PKHA_ArithEccMul; params.equalTime = equalTime; params.arithType = arithType; params.r2modn = R2modN ? kLTC_PKHA_InputR2 : kLTC_PKHA_CalcR2; clearMask = kLTC_ClearMode; /* Clear internal register states. */ clearMask |= kLTC_ClearPkhaSizeA; clearMask |= kLTC_ClearPkhaSizeB; clearMask |= kLTC_ClearPkhaSizeN; clearMask |= kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = kLTC_StatusDoneIsr; ltc_pkha_clear_regabne(base, true, true, true, true); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKESZ = sizeE; ltc_pkha_write_reg(base, kLTC_PKHA_RegE, 0, E, sizeE); base->PKASZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A->X, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 1, A->Y, size); ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); if (R2modN) { ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 1, R2modN, size); } status = ltc_pkha_init_mode(base, ¶ms); if (status == kStatus_Success) { /* Read the data from the result register into place. */ ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 1, result->X, size); ltc_pkha_read_reg(base, kLTC_PKHA_RegB, 2, result->Y, size); if (infinity) { *infinity = (bool)(base->STA & kLTC_StatusPublicKeyOpZero); } } ltc_clear_all(base, true); return status; } #endif /* FSL_FEATURE_LTC_HAS_PKHA */