/* * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. * Copyright 2016-2020 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #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 = (0U != (temp32 & LTC_STA_EI_MASK)) ? true : false; done = (0U != (temp32 & LTC_STA_DI_MASK)) ? true : false; } 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 = (uint32_t)kLTC_ClearDataSize; /* Clear 'done' interrupt status. This also clears the mode register. */ base->STA = (uint32_t)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) { (void)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 (0U != 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 (0U != ((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 *)(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 *)(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)) || defined(__CC_ARM)) register uint8_t *dst = dstAddr; /* Cortex M0 does not support misaligned stores */ if (0U != ((uint32_t)dst & 0x3u)) { *dst++ = (uint8_t)(srcWord & 0x000000FFU); *dst++ = (uint8_t)((srcWord & 0x0000FF00U) >> 8U); *dst++ = (uint8_t)((srcWord & 0x00FF0000U) >> 16U); *dst++ = (uint8_t)((srcWord & 0xFF000000U) >> 24U); } else { *((uint32_t *)(uint32_t)dstAddr) = srcWord; /* addr aligned to 0-modulo-4 so it is safe to type cast */ } #else *((uint32_t *)(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). */ static void ltc_set_key(LTC_Type *base, const uint8_t *key, uint8_t keySize) { uint32_t i; for (i = 0; i < ((uint32_t)keySize / 4u); i++) { base->KEY[i] = ltc_get_word_from_unaligned(&key[i * sizeof(uint32_t)]); } } /*! * @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 */ static void ltc_get_key(LTC_Type *base, uint8_t *key, uint8_t keySize) { uint32_t i; for (i = 0; i < ((uint32_t)keySize / 4U); i++) { ltc_set_unaligned_from_word(base->KEY[i], &key[i * sizeof(uint32_t)]); } } /*! * @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) { uint32_t i; uint8_t j; uint8_t szLeft; /* Context register is 16 words in size (64 bytes). Ensure we are only * writing a valid amount of data. */ if (startIndex + (dataSize / 4u) >= 16u) { return kStatus_InvalidArgument; } j = 0; szLeft = dataSize % 4u; for (i = (uint32_t)startIndex; i < ((uint32_t)startIndex + (uint32_t)dataSize / 4u); i++) { base->CTX[i] = ltc_get_word_from_unaligned(&data[j]); j += (uint8_t)sizeof(uint32_t); } if (0U != szLeft) { uint32_t context_data = {0}; ltc_memcpy(&context_data, &data[j], (uint32_t)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) { uint32_t i; int32_t j; uint8_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 / 4u) >= 16u) { return kStatus_InvalidArgument; } j = 0; szLeft = dataSize % 4u; for (i = (uint32_t)startIndex; i < ((uint32_t)startIndex + (uint32_t)dataSize / 4u); i++) { ltc_set_unaligned_from_word(base->CTX[i], &dest[j]); j += (int32_t)sizeof(uint32_t); } if (0U != szLeft) { rdCtx = base->CTX[i]; ltc_memcpy(&dest[j], &rdCtx, (uint32_t)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 |= (uint32_t)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 = (uint32_t)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 > 0u) || (inSize > 0u)) { fifoStatus = base->FIFOSTA; /* Check output FIFO level to make sure there is at least an entry * ready to be read. */ if (0U != (fifoStatus & LTC_FIFOSTA_OFL_MASK)) { /* Read data from the output FIFO. */ if (outSize > 0u) { if (outSize >= sizeof(uint32_t)) { ltc_set_unaligned_from_word(base->OFIFO, out); out = &out[sizeof(uint32_t)]; outSize -= sizeof(uint32_t); } else /* (outSize > 0) && (outSize < 4) */ { fifoData = base->OFIFO; ltc_memcpy(out, &fifoData, outSize); out = &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 (0U == (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 > 0u) { if (inSize >= sizeof(uint32_t)) { base->IFIFO = ltc_get_word_from_unaligned(in); inSize -= sizeof(uint32_t); in = &in[sizeof(uint32_t)]; } else /* (inSize > 0) && (inSize < 4) */ { fifoData = 0; ltc_memcpy(&fifoData, in, inSize); base->IFIFO = fifoData; in = &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 ((NULL == inData) || (NULL == 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 == 0u) { lastSize = 16; inSize -= 16u; } 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 value will be used if message is split into multiple LTC_FIFO_SZ_MAX_DOWN_ALGN chunks */ /* in case of AES CBC and AES ECB decryption */ /* the conversion of AES forward key to AES reverse key happens with the 1st chunk */ /* so all the following chunks already have the reverse key */ /* thus we add the MD register bit 12 to the MD value to notify the AES engine that the key is the AES reverse key */ /* This really needs to be done only for AES ECB and CBC Decrypt as these are the only */ /* cipher block modes that use AES reverse key */ modeReg = base->MD; /* AES CBC Decrypt */ if (modeReg == 0x00100100u) { /* add MSB of AAI - DK (Decrypt Key) bit */ modeReg = 0x00101100u; } /* AES ECB Decrypt */ if (modeReg == 0x00100200u) { /* add MSB of AAI - DK (Decrypt Key) bit */ modeReg = 0x00101200u; } retval = kStatus_Success; while (0U != 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 = &inData[sz]; inSize -= sz; outData = &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 (0U != (num_bytes % 4u)) { words++; } if (words > 4u) { 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 (0U != dataSize) { if (dataSize > 16u) { ltc_memcpy(&blk, data, 16u); dataSize -= 16u; data = &data[16]; } 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, (const ltc_xcm_block_t *)(uint32_t)&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 ((NULL == inData) || (NULL == outData)) { return kStatus_InvalidArgument; } if (!(((uint8_t)kLTC_ModeFinalize == (uint8_t)lastAs) || ((uint8_t)kLTC_ModeInitFinal == (uint8_t)lastAs))) { return kStatus_InvalidArgument; } if (0u == inSize) { return kStatus_Success; } if (inSize <= 16u) { fsm = lastAs; lastSize = inSize; } else { fsm = (ltc_mode_algorithm_state_t)(uint32_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 (0U != lastSize) { if ((fsm == kLTC_ModeUpdate) || (fsm == kLTC_ModeInit)) { while (0U != 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 (0U != 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; } } } else if ((fsm == kLTC_ModeFinalize) || (fsm == 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; } else { /*do nothing*/ } } 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 = (uint32_t)kLTC_ClearDataSize; base->STA = (uint32_t)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, (const ltc_xcm_block_t *)(uint32_t)&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 (NULL != tag) { /* For decrypt, compare received MAC with the computed MAC. */ if ((uint32_t)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 */ status = ltc_get_context(base, &tag[0], (uint8_t)tagSize, (uint8_t)ctx); } } return status; } /******************************************************************************* * LTC Common code public ******************************************************************************/ /*! * brief Initializes the LTC driver. * This function initializes the LTC driver. * param base LTC peripheral base address */ void LTC_Init(LTC_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) #if !(defined(FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT) && FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT) /* ungate clock */ CLOCK_EnableClock(kCLOCK_Ltc0); #endif /* FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT */ #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } /*! * brief Deinitializes the LTC driver. * This function deinitializes the LTC driver. * param base LTC peripheral base address */ void LTC_Deinit(LTC_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) #if !(defined(FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT) && FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT) /* gate clock */ CLOCK_DisableClock(kCLOCK_Ltc0); #endif /* FSL_FEATURE_LTC_HAS_NO_CLOCK_CONTROL_BIT */ #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } #if defined(FSL_FEATURE_LTC_HAS_DPAMS) && FSL_FEATURE_LTC_HAS_DPAMS /*! * brief Sets the DPA Mask Seed register. * * The DPA Mask Seed register reseeds the mask that provides resistance against DPA (differential power analysis) * attacks on AES or DES keys. * * Differential Power Analysis Mask (DPA) resistance uses a randomly changing mask that introduces * "noise" into the power consumed by the AES or DES. This reduces the signal-to-noise ratio that differential * power analysis attacks use to "guess" bits of the key. This randomly changing mask should be * seeded at POR, and continues to provide DPA resistance from that point on. However, to provide even more * DPA protection it is recommended that the DPA mask be reseeded after every 50,000 blocks have * been processed. At that time, software can opt to write a new seed (preferably obtained from an RNG) * into the DPA Mask Seed register (DPAMS), or software can opt to provide the new seed earlier or * later, or not at all. DPA resistance continues even if the DPA mask is never reseeded. * * param base LTC peripheral base address * param mask The DPA mask seed. */ 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. */ retval = ltc_symmetric_update(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeECB, kLTC_ModeDecrypt); if (kStatus_Success != retval) { return retval; } /* set DK bit in the LTC Mode Register AAI field for directly loaded decrypt keys */ if (keyType == kLTC_DecryptKey) { uint32_t u32mask = 1; base->MD |= (u32mask << (uint32_t)kLTC_ModeRegBitShiftDK); } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &ciphertext[0], size, &plaintext[0]); return retval; } /******************************************************************************* * AES Code public ******************************************************************************/ /*! * brief Transforms an AES encrypt key (forward AES) into the decrypt key (inverse AES). * * Transforms the AES encrypt key (forward AES) into the decrypt key (inverse AES). * The key derived by this function can be used as a direct load decrypt key * for AES ECB and CBC decryption operations (keyType argument). * * param base LTC peripheral base address * param encryptKey Input key for decrypt key transformation * param[out] decryptKey Output key, the decrypt form of the AES key. * param keySize Size of the input key and output key in bytes. Must be 16, 24, or 32. * return Status from key generation operation */ 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] = {0}; uint8_t ciphertext[LTC_AES_BLOCK_SIZE] = {0}; 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, (uint8_t)keySize); ltc_clear_all(base, false); return status; } /*! * brief Encrypts AES using the ECB block mode. * * Encrypts AES using the ECB block mode. * * param base LTC peripheral base address * param plaintext Input plain text to encrypt * param[out] ciphertext Output cipher text * param size Size of input and output data in bytes. Must be multiple of 16 bytes. * param key Input key to use for encryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * return Status from encrypt operation */ 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) || (0U != (size % 16u))) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ retval = ltc_symmetric_update(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeECB, kLTC_ModeEncrypt); if (kStatus_Success != retval) { return retval; } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &plaintext[0], size, &ciphertext[0]); ltc_clear_all(base, false); return retval; } /*! * brief Decrypts AES using ECB block mode. * * Decrypts AES using ECB block mode. * * param base LTC peripheral base address * param ciphertext Input cipher text to decrypt * param[out] plaintext Output plain text * param size Size of input and output data in bytes. Must be multiple of 16 bytes. * param key Input key. * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param keyType Input type of the key (allows to directly load decrypt key for AES ECB decrypt operation.) * return Status from decrypt operation */ 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) || (0U != (size % 16u))) { return kStatus_InvalidArgument; } status = ltc_aes_decrypt_ecb(base, ciphertext, plaintext, size, key, keySize, keyType); ltc_clear_all(base, false); return status; } /*! * brief Encrypts AES using CBC block mode. * * param base LTC peripheral base address * param plaintext Input plain text to encrypt * param[out] ciphertext Output cipher text * param size Size of input and output data in bytes. Must be multiple of 16 bytes. * param iv Input initial vector to combine with the first input block. * param key Input key to use for encryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * return Status from encrypt operation */ 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) || (0U != (size % 16u))) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ retval = ltc_symmetric_update(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeCBC, kLTC_ModeEncrypt); if (kStatus_Success != retval) { return retval; } /* Write IV data to the context register. */ retval = ltc_set_context(base, &iv[0], LTC_AES_IV_SIZE, 0); if (kStatus_Success != retval) { return retval; } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &plaintext[0], size, &ciphertext[0]); ltc_clear_all(base, false); return retval; } /*! * brief Decrypts AES using CBC block mode. * * param base LTC peripheral base address * param ciphertext Input cipher text to decrypt * param[out] plaintext Output plain text * param size Size of input and output data in bytes. Must be multiple of 16 bytes. * param iv Input initial vector to combine with the first input block. * param key Input key to use for decryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param keyType Input type of the key (allows to directly load decrypt key for AES CBC decrypt operation.) * return Status from decrypt operation */ 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) || (0U != (size % 16u))) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ retval = ltc_symmetric_update(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeCBC, kLTC_ModeDecrypt); if (kStatus_Success != retval) { return retval; } /* Write IV data to the context register. */ retval = ltc_set_context(base, &iv[0], LTC_AES_IV_SIZE, 0); if (kStatus_Success != retval) { return retval; } /* set DK bit in the LTC Mode Register AAI field for directly loaded decrypt keys */ if (keyType == kLTC_DecryptKey) { uint32_t u32mask = 1; base->MD |= (u32mask << (uint8_t)kLTC_ModeRegBitShiftDK); } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, &ciphertext[0], size, &plaintext[0]); ltc_clear_all(base, false); return retval; } /*! * brief Encrypts or decrypts AES using CTR block mode. * * Encrypts or decrypts AES using CTR block mode. * AES CTR mode uses only forward AES cipher and same algorithm for encryption and decryption. * The only difference between encryption and decryption is that, for encryption, the input argument * is plain text and the output argument is cipher text. For decryption, the input argument is cipher text * and the output argument is plain text. * * param base LTC peripheral base address * param input Input data for CTR block mode * param[out] output Output data for CTR block mode * param size Size of input and output data in bytes * param[in,out] counter Input counter (updates on return) * param key Input key to use for forward AES cipher * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param[out] counterlast Output cipher of last counter, for chained CTR calls. NULL can be passed if chained calls are * not used. * param[out] szLeft Output number of bytes in left unused in counterlast block. NULL can be passed if chained calls * are not used. * return Status from encrypt operation */ 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. */ retval = ltc_symmetric_update(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeCTR, kLTC_ModeEncrypt); if (kStatus_Success != retval) { return retval; } /* Write initial counter data to the context register. * NOTE the counter values start at 4-bytes offset into the context. */ retval = ltc_set_context(base, &counter[0], 16U, 4U); if (kStatus_Success != retval) { return retval; } /* Process data and return status. */ retval = ltc_process_message_in_sessions(base, input, size, output); if (kStatus_Success != retval) { return retval; } input = &input[size]; output = &output[size]; if ((counterlast != NULL) && (0U != 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 (NULL != 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); } (void)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 (NULL == base) { return kStatus_InvalidArgument; } /* tag can be NULL to skip tag processing */ if ((NULL == key) || ((0U != ivSize) && (NULL == iv)) || ((0U != aadSize) && (NULL == aad)) || ((0U != inputSize) && ((NULL == src) || (NULL == 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 (0U == (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 == 0U)) { return kStatus_InvalidArgument; } sz = LTC_FIFO_SZ_MAX_DOWN_ALGN; next_size_func = type == LTC_AES_GCM_TYPE_AAD ? aadsize_next : ivsize_next; while (0U != 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 |= (uint32_t)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) { (void)ltc_symmetric_final(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeGCM, encryptMode); modeReg = base->MD; iv_only = (aadSize == 0U) && (inputSize == 0U); aad_only = (inputSize == 0U); /* 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 != 0U) { 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 && (0U != aadSize)) { retval = ltc_wait(base); if (kStatus_Success != retval) { return retval; } } if (0U != inputSize) { /* Workaround for the LTC Data Size register update errata TKT261180 */ while (16U < base->DS) { } (void)ltc_symmetric_process_data(base, &src[0], inputSize, &dst[0]); } } else { retval = ltc_symmetric_init(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeGCM, encryptMode); if (kStatus_Success != retval) { return retval; } modeReg = base->MD; /* process IV */ if (0U != 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, (uint32_t)kLTC_ModeInit); if (kStatus_Success != retval) { return retval; } } /* process AAD */ if (0U != aadSize) { /* AS mode to process last chunk of AAD. it differs if we are in GMAC or GCM */ ltc_mode_t lastModeReg; if (0U == inputSize) { /* if there is no DATA, set mode to compute final MAC. this is GMAC mode */ lastModeReg = (uint32_t)kLTC_ModeInitFinal; } else { /* there are confidential DATA. so process last chunk of AAD in UPDATE mode */ lastModeReg = (uint32_t)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 (0U != inputSize) { /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)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 ******************************************************************************/ /*! * brief Encrypts AES and tags using GCM block mode. * * Encrypts AES and optionally tags using GCM block mode. If plaintext is NULL, only the GHASH is calculated and output * in the 'tag' field. * * param base LTC peripheral base address * param plaintext Input plain text to encrypt * param[out] ciphertext Output cipher text. * param size Size of input and output data in bytes * param iv Input initial vector * param ivSize Size of the IV * param aad Input additional authentication data * param aadSize Input size in bytes of AAD * param key Input key to use for encryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param[out] tag Output hash tag. Set to NULL to skip tag processing. * param tagSize Input size of the tag to generate, in bytes. Must be 4,8,12,13,14,15 or 16. * return Status from encrypt operation */ 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; } /*! * brief Decrypts AES and authenticates using GCM block mode. * * Decrypts AES and optionally authenticates using GCM block mode. If ciphertext is NULL, only the GHASH is calculated * and compared with the received GHASH in 'tag' field. * * param base LTC peripheral base address * param ciphertext Input cipher text to decrypt * param[out] plaintext Output plain text. * param size Size of input and output data in bytes * param iv Input initial vector * param ivSize Size of the IV * param aad Input additional authentication data * param aadSize Input size in bytes of AAD * param key Input key to use for encryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param tag Input hash tag to compare. Set to NULL to skip tag processing. * param tagSize Input size of the tag, in bytes. Must be 4, 8, 12, 13, 14, 15, or 16. * return Status from decrypt operation */ 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 (NULL != 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 (NULL == base) { return kStatus_InvalidArgument; } /* tag can be NULL to skip tag processing */ if ((NULL == src) || (NULL == iv) || (NULL == key) || (NULL == 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 > 0u) && (tagSize < 4u)) || (tagSize > 16u) || (0U != (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) << 24U) | ((in & 0x0000ff00u) << 8U) | ((in & 0x00ff0000u) >> 8U) | ((in & 0xff000000u) >> 24U)); } 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}}; uint32_t 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 = 15u - ivSize; flags = (uint8_t)(8u * ((tagSize - 2u) / 2u) + q - 1u); /* 8*M' + L' */ if (0U != aadSize) { flags |= 0x40U; /* 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. */ (void)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] = (uint8_t)(q - 1u); /* flags field */ ltc_memcpy(&blk.b[1], iv, ivSize); /* nonce field */ (void)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 (0U != 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((true == aad_only ? 1U : 0U)) | ((aadSize + 2U) & LTC_DS_DS_MASK); /* move first AAD block (16 bytes block B1) to FIFO */ ltc_move_block_to_ififo(base, (const ltc_xcm_block_t *)(uint32_t)&blk, sizeof(blk)); } else { base->AADSZ = LTC_AADSZ_AL(1U) | (16U); /* move first AAD block (16 bytes block B1) to FIFO */ ltc_move_block_to_ififo(base, (const ltc_xcm_block_t *)(uint32_t)&blk, sizeof(blk)); } /* track consumed AAD. sz bytes have been moved to fifo. */ aadSize -= sz; aad = &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 (0U != 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((true == aad_only ? 1U : 0U)) | (aadSize & LTC_DS_DS_MASK); ltc_move_to_ififo(base, aad, aadSize); aadSize = 0; } else { base->AADSZ = LTC_AADSZ_AL(1U) | (sz & LTC_DS_DS_MASK); ltc_move_to_ififo(base, aad, sz); aadSize -= sz; aad = &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 + 2u) + 15u) & 0xfffffff0u) + inputSize) <= max_ltc_fifo_sz; /* setup key, algorithm and set the alg.state to INITIALIZE */ if (single_ses_proc_all) { retval = ltc_symmetric_init_final(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeCCM, encryptMode); if (kStatus_Success != retval) { return retval; } } else { retval = ltc_symmetric_init(base, key, (uint8_t)keySize, kLTC_AlgorithmAES, kLTC_ModeCCM, encryptMode); if (kStatus_Success != retval) { return retval; } } 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 (0U != 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 ******************************************************************************/ /*! * brief Encrypts AES and tags using CCM block mode. * * Encrypts AES and optionally tags using CCM block mode. * * param base LTC peripheral base address * param plaintext Input plain text to encrypt * param[out] ciphertext Output cipher text. * param size Size of input and output data in bytes. Zero means authentication only. * param iv Nonce * param ivSize Length of the Nonce in bytes. Must be 7, 8, 9, 10, 11, 12, or 13. * param aad Input additional authentication data. Can be NULL if aadSize is zero. * param aadSize Input size in bytes of AAD. Zero means data mode only (authentication skipped). * param key Input key to use for encryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param[out] tag Generated output tag. Set to NULL to skip tag processing. * param tagSize Input size of the tag to generate, in bytes. Must be 4, 6, 8, 10, 12, 14, or 16. * return Status from encrypt operation */ 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; } /*! * brief Decrypts AES and authenticates using CCM block mode. * * Decrypts AES and optionally authenticates using CCM block mode. * * param base LTC peripheral base address * param ciphertext Input cipher text to decrypt * param[out] plaintext Output plain text. * param size Size of input and output data in bytes. Zero means authentication only. * param iv Nonce * param ivSize Length of the Nonce in bytes. Must be 7, 8, 9, 10, 11, 12, or 13. * param aad Input additional authentication data. Can be NULL if aadSize is zero. * param aadSize Input size in bytes of AAD. Zero means data mode only (authentication skipped). * param key Input key to use for decryption * param keySize Size of the input key, in bytes. Must be 16, 24, or 32. * param tag Received tag. Set to NULL to skip tag processing. * param tagSize Input size of the received tag to compare with the computed tag, in bytes. Must be 4, 6, 8, 10, 12, * 14, or 16. * return Status from decrypt operation */ 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 (NULL != 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) || (0U != (size % 8u)))) { return kStatus_InvalidArgument; } /* Initialize algorithm state. */ retval = ltc_symmetric_update(base, &key[0], LTC_DES_KEY_SIZE, kLTC_AlgorithmDES, modeAs, modeEnc); if (kStatus_Success != retval) { return retval; } if ((modeAs != kLTC_ModeECB)) { retval = ltc_set_context(base, iv, LTC_DES_IV_SIZE, 0); if (kStatus_Success != retval) { return retval; } } /* 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) || (0U != (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 (NULL != key3) { ltc_memcpy(&key[LTC_DES_KEY_SIZE * 2], &key3[0], LTC_DES_KEY_SIZE); keySize = (uint8_t)sizeof(key); } /* Initialize algorithm state. */ retval = ltc_symmetric_update(base, &key[0], keySize, kLTC_Algorithm3DES, modeAs, modeEnc); if (kStatus_Success != retval) { return retval; } if ((modeAs != kLTC_ModeECB)) { retval = ltc_set_context(base, iv, LTC_DES_IV_SIZE, 0); if (kStatus_Success != retval) { return retval; } } /* 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 ******************************************************************************/ /*! * brief Encrypts DES using ECB block mode. * * Encrypts DES using ECB block mode. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key Input key to use for encryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts DES using ECB block mode. * * Decrypts DES using ECB block mode. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key Input key to use for decryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts DES using CBC block mode. * * Encrypts DES using CBC block mode. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Ouput ciphertext * param size Size of input and output data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key Input key to use for encryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts DES using CBC block mode. * * Decrypts DES using CBC block mode. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key Input key to use for decryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts DES using CFB block mode. * * Encrypts DES using CFB block mode. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param size Size of input data in bytes * param iv Input initial block. * param key Input key to use for encryption * param[out] ciphertext Output ciphertext * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts DES using CFB block mode. * * Decrypts DES using CFB block mode. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input initial block. * param key Input key to use for decryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts DES using OFB block mode. * * Encrypts DES using OFB block mode. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key Input key to use for encryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts DES using OFB block mode. * * Decrypts DES using OFB block mode. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key Input key to use for decryption * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using ECB block mode with two keys. * * Encrypts triple DES using ECB block mode with two keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using ECB block mode with three keys. * * Encrypts triple DES using ECB block mode with three keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using ECB block mode with two keys. * * Decrypts triple DES using ECB block mode with two keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using ECB block mode with three keys. * * Decrypts triple DES using ECB block mode with three keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes. Must be multiple of 8 bytes. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using CBC block mode with two keys. * * Encrypts triple DES using CBC block mode with two keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using CBC block mode with three keys. * * Encrypts triple DES using CBC block mode with three keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using CBC block mode with two keys. * * Decrypts triple DES using CBC block mode with two keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using CBC block mode with three keys. * * Decrypts triple DES using CBC block mode with three keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input initial vector to combine with the first plaintext block. * The iv does not need to be secret, but it must be unpredictable. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using CFB block mode with two keys. * * Encrypts triple DES using CFB block mode with two keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes * param iv Input initial block. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using CFB block mode with three keys. * * Encrypts triple DES using CFB block mode with three keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and ouput data in bytes * param iv Input initial block. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using CFB block mode with two keys. * * Decrypts triple DES using CFB block mode with two keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input initial block. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using CFB block mode with three keys. * * Decrypts triple DES using CFB block mode with three keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input data in bytes * param iv Input initial block. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using OFB block mode with two keys. * * Encrypts triple DES using OFB block mode with two keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Encrypts triple DES using OFB block mode with three keys. * * Encrypts triple DES using OFB block mode with three keys. * * param base LTC peripheral base address * param plaintext Input plaintext to encrypt * param[out] ciphertext Output ciphertext * param size Size of input and output data in bytes * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using OFB block mode with two keys. * * Decrypts triple DES using OFB block mode with two keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key1 First input key for key bundle * param key2 Second input key for key bundle * return Status from encrypt/decrypt operation */ 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); } /*! * brief Decrypts triple DES using OFB block mode with three keys. * * Decrypts triple DES using OFB block mode with three keys. * * param base LTC peripheral base address * param ciphertext Input ciphertext to decrypt * param[out] plaintext Output plaintext * param size Size of input and output data in bytes * param iv Input unique input vector. The OFB mode requires that the IV be unique * for each execution of the mode under the given key. * param key1 First input key for key bundle * param key2 Second input key for key bundle * param key3 Third input key for key bundle * return Status from encrypt/decrypt operation */ 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: /* All the cases have been listed above, the default clause should not be reached. */ break; } modeReg |= (uint32_t)asMode; if (NULL != 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; (void)ltc_symmetric_init(base, key, (uint8_t)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 = 12U * sizeof(uint32_t); break; case kLTC_Cmac: /* * word[0-3] = mac * word[3-7] = L */ sz = 8u * 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; } (void)ltc_get_context(base, (uint8_t *)&ctx->word[0], (uint8_t)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], (uint8_t)ctx->word[(uint8_t)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 = 12U * sizeof(uint32_t); break; case kLTC_Cmac: /* * word[0-3] = mac * word[3-7] = L */ sz = 8u * 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; } (void)ltc_set_context(base, (const uint8_t *)&ctx->word[0], (uint8_t)sz, 0); if (ltc_hash_alg_is_cmac(ctx->algo)) { /* * word[12-19] = key * word[kLtcCmacCtxKeySize] = keySize */ base->CW = (uint32_t)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], (uint8_t)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 = (uint32_t)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 (0U != (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 (0U != dataSize) { if (dataSize >= blockSize) { ltc_memcpy(&ctx->blk, data, blockSize); ltc_hash_block_to_ififo(ctx->base, (const ltc_hash_block_t *)(uint32_t)&ctx->blk, blockSize, blockSize); dataSize -= blockSize; data = &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 (0U != ctx->blksz) { sz = blockSize - ctx->blksz; if (sz > inputSize) { sz = inputSize; } ltc_memcpy(ctx->blk.b + ctx->blksz, input, sz); input = &input[sz]; inputSize -= sz; ctx->blksz += sz; if (ctx->blksz == blockSize) { base->DS = blockSize; ltc_hash_block_to_ififo(base, (const ltc_hash_block_t *)(uint32_t)&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 (0U != inputSize) { /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)kLTC_ModeUpdate; base->MD = modeReg; } } } if (NULL != 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 (0U != dataSize) { if (dataSize > blockSize) { dataSize -= blockSize; data = &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 = &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 == 0u) { 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 = &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 (0U != inputSize) { if (inputSize < sz) { uint32_t lastSize; lastSize = inputSize % blockSize; if (lastSize == 0u) { lastSize = blockSize; } inputSize -= lastSize; if (0U != 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 = &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 = &input[sz]; status = ltc_wait(base); if (kStatus_Success != status) { return status; } /* set algorithm state to UPDATE */ modeReg &= ~LTC_MD_AS_MASK; modeReg |= (uint32_t)kLTC_ModeUpdate; base->MD = modeReg; } } /* end while */ return status; } /******************************************************************************* * HASH Code public ******************************************************************************/ /*! * brief Initialize HASH context * * This function initialize the HASH. * Key shall be supplied if the underlaying algoritm is AES XCBC-MAC or CMAC. * Key shall be NULL if the underlaying algoritm is SHA. * * For XCBC-MAC, the key length must be 16. For CMAC, the key length can be * the AES key lengths supported by AES engine. For MDHA the key length argument * is ignored. * * param base LTC peripheral base address * param[out] ctx Output hash context * param algo Underlaying algorithm to use for hash computation. * param key Input key (NULL if underlaying algorithm is SHA) * param keySize Size of input key in bytes * return Status of initialization */ 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 *)(uint32_t)ctx; ctxInternal->algo = algo; for (i = 0; i < (uint32_t)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; uint32_t j; j = sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); for (i = 0; i < j; i++) { ctxInternal->blk.w[0] = 0u; } ctxInternal->state = kLTC_HashInit; ctxInternal->base = base; return kStatus_Success; } /*! * brief Add data to current HASH * * Add data to current HASH. This can be called repeatedly with an arbitrary amount of data to be * hashed. * * param[in,out] ctx HASH context * param input Input data * param inputSize Size of input data in bytes * return Status of the hash update operation */ 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 *)(uint32_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 = (uint32_t)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; } /*! * brief Finalize hashing * * Outputs the final hash and erases the context. * * param[in,out] ctx Input hash context * param[out] output Output hash data * param[out] outputSize Output parameter storing the size of the output hash in bytes * return Status of the hash finish operation */ 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 *)(uint32_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 = (uint32_t)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, (const ltc_hash_block_t *)(uint32_t)&ctxInternal->blk, ctxInternal->blksz, ltc_hash_get_block_size(ctxInternal->algo)); /* Wait for finish of the encryption */ status = ltc_wait(base); if (NULL != outputSize) { if (algOutSize < *outputSize) { *outputSize = algOutSize; } else { algOutSize = *outputSize; } } (void)ltc_get_context(base, &output[0], (uint8_t)algOutSize, 0u); ctxW = (uint32_t *)(uint32_t)ctx; for (i = 0; i < (uint32_t)LTC_HASH_CTX_SIZE; i++) { ctxW[i] = 0u; } ltc_clear_all(base, false); return status; } /*! * brief Create HASH on given data * * Perform the full keyed HASH in one function call. * * param base LTC peripheral base address * param algo Block cipher algorithm to use for CMAC creation * param input Input data * param inputSize Size of input data in bytes * param key Input key * param keySize Size of input key in bytes * param[out] output Output hash data * param[out] outputSize Output parameter storing the size of the output hash in bytes * return Status of the one call hash operation. */ 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 |= ((uint32_t)1U << 19U); } if (B) { mode |= ((uint32_t)1U << 18U); } if (N) { mode |= ((uint32_t)1U << 16U); } if (E) { mode |= ((uint32_t)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: /* All the cases have been listed above, the default clause should not be reached. */ 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 > 0U) { 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 > 0U) { 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 = (uint32_t)kLTC_ClearMode; /* clear Mode Register */ /* Clear internal register states. */ if (0U != sizeA) { clearMask |= (uint32_t)kLTC_ClearPkhaSizeA; } if (0U != sizeB) { clearMask |= (uint32_t)kLTC_ClearPkhaSizeB; } if (0U != sizeN) { clearMask |= (uint32_t)kLTC_ClearPkhaSizeN; } if (0U != sizeE) { clearMask |= (uint32_t)kLTC_ClearPkhaSizeE; } base->CW = clearMask; base->STA = (uint32_t)kLTC_StatusDoneIsr; (void)ltc_pkha_clear_regabne(base, (bool)(uintptr_t)A, (bool)(uintptr_t)B, (bool)(uintptr_t)N, (bool)(uintptr_t)E); /* Write register sizes. */ /* Write modulus (N) and A and B register arguments. */ if (0U != sizeN) { base->PKNSZ = sizeN; if (NULL != N) { (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, sizeN); } } if (0U != sizeA) { base->PKASZ = sizeA; if (NULL != A) { (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A, sizeA); } } if (0U != sizeB) { base->PKBSZ = sizeB; if (NULL != B) { (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, B, sizeB); } } if (0U != sizeE) { base->PKESZ = sizeE; if (NULL != E) { (void)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)(((uint32_t)reg) >> 1u); i++; } while (0U != (uint32_t)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)(((uint32_t)reg) >> 1u); i++; } while (0U != (uint32_t)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 = (uint32_t)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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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 ******************************************************************************/ /*! * brief Compare two PKHA big numbers. * * Compare two PKHA big numbers. Return 1 for a > b, -1 for a < b and 0 if they are same. * PKHA big number is lsbyte first. Thus the comparison starts at msbyte which is the last member of tested arrays. * * param a First integer represented as an array of bytes, lsbyte first. * param sizeA Size in bytes of the first integer. * param b Second integer represented as an array of bytes, lsbyte first. * param sizeB Size in bytes of the second integer. * return 1 if a > b. * return -1 if a < b. * return 0 if a = b. */ 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 ((0U != sizeA) && (0u == a[sizeA - 1U])) { sizeA--; } /* skip zero msbytes - integer b */ while ((0U != sizeB) && (0u == b[sizeB - 1U])) { 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 == 0U) { retval = 0; } /* sizeA = sizeB = 0 */ else { int n; int i; int val; uint32_t equal; n = (int)sizeA - 1; i = 0; equal = 0; while (n >= 0) { uint32_t chXor = ((uint32_t)a[i] ^ (uint32_t)b[i]); equal |= chXor; val = (int)chXor * ((int)a[i] - (int)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 (0U == equal) { retval = 0; } } return (retval); } /*! * brief Converts from integer to Montgomery format. * * This function computes R2 mod N and optionally converts A or B into Montgomery format of A or B. * * param base LTC peripheral base address * param N modulus * param sizeN size of N in bytes * param[in,out] A The first input in non-Montgomery format. Output Montgomery format of the first input. * param[in,out] sizeA pointer to size variable. On input it holds size of input A in bytes. On output it holds size of * Montgomery format of A in bytes. * param[in,out] B Second input in non-Montgomery format. Output Montgomery format of the second input. * param[in,out] sizeB pointer to size variable. On input it holds size of input B in bytes. On output it holds size of * Montgomery format of B in bytes. * param[out] R2 Output Montgomery factor R2 mod N. * param[out] sizeR2 pointer to size variable. On output it holds size of Montgomery factor R2 mod N in bytes. * param equalTime Run the function time equalized or no timing equalization. * param arithType Type of arithmetic to perform (integer or F2m) * return Operation status. */ 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 ((NULL != N) && (0U != sizeN) && (NULL != R2) && (NULL != 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 ((NULL != A) && (NULL != 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 ((NULL != B) && (NULL != 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; } /*! * brief Converts from Montgomery format to int. * * This function converts Montgomery format of A or B into int A or B. * * param base LTC peripheral base address * param N modulus. * param sizeN size of N modulus in bytes. * param[in,out] A Input first number in Montgomery format. Output is non-Montgomery format. * param[in,out] sizeA pointer to size variable. On input it holds size of the input A in bytes. On output it holds * size of non-Montgomery A in bytes. * param[in,out] B Input first number in Montgomery format. Output is non-Montgomery format. * param[in,out] sizeB pointer to size variable. On input it holds size of the input B in bytes. On output it holds * size of non-Montgomery B in bytes. * param equalTime Run the function time equalized or no timing equalization. * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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 ((NULL != A) && (NULL != sizeA)) { status = ltc_pkha_modmul(base, A, *sizeA, &one, (uint16_t)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 ((NULL != B) && (NULL != sizeB)) { status = ltc_pkha_modmul(base, B, *sizeB, &one, (uint16_t)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; } /*! * brief Performs modular addition - (A + B) mod N. * * This function performs modular addition of (A + B) mod N, with either * integer or binary polynomial (F2m) inputs. In the F2m form, this function is * equivalent to a bitwise XOR and it is functionally the same as subtraction. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param B second addend (integer or binary polynomial) * param sizeB Size of B in bytes * param N modulus. For F2m operation this can be NULL, as N is ignored during F2m polynomial addition. * param sizeN Size of N in bytes. This must be given for both integer and F2m polynomial additions. * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Performs modular subtraction - (A - B) mod N. * * This function performs modular subtraction of (A - B) mod N with * integer inputs. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param B second addend (integer or binary polynomial) * param sizeB Size of B in bytes * param N modulus * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Performs modular subtraction - (B - A) mod N. * * This function performs modular subtraction of (B - A) mod N, * with integer inputs. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param B second addend (integer or binary polynomial) * param sizeB Size of B in bytes * param N modulus * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Performs modular multiplication - (A x B) mod N. * * This function performs modular multiplication with either integer or * binary polynomial (F2m) inputs. It can optionally specify whether inputs * and/or outputs will be in Montgomery form or not. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param B second addend (integer or binary polynomial) * param sizeB Size of B in bytes * param N modulus. * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * param montIn Format of inputs * param montOut Format of output * param equalTime Run the function time equalized or no timing equalization. This argument is ignored for F2m modular * multiplication. * return Operation 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; } /*! * brief Performs modular exponentiation - (A^E) mod N. * * This function performs modular exponentiation with either integer or * binary polynomial (F2m) inputs. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param N modulus * param sizeN Size of N in bytes * param E exponent * param sizeE Size of E in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param montIn Format of A input (normal or Montgomery) * param arithType Type of arithmetic to perform (integer or F2m) * param equalTime Run the function time equalized or no timing equalization. * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Performs modular reduction - (A) mod N. * * This function performs modular reduction with either integer or * binary polynomial (F2m) inputs. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param N modulus * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Performs modular inversion - (A^-1) mod N. * * This function performs modular inversion with either integer or * binary polynomial (F2m) inputs. * * param base LTC peripheral base address * param A first addend (integer or binary polynomial) * param sizeA Size of A in bytes * param N modulus * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Computes integer Montgomery factor R^2 mod N. * * This function computes a constant to assist in converting operands * into the Montgomery residue system representation. * * param base LTC peripheral base address * param N modulus * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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; } /*! * brief Calculates the greatest common divisor - GCD (A, N). * * This function calculates the greatest common divisor of two inputs with * either integer or binary polynomial (F2m) inputs. * * param base LTC peripheral base address * param A first value (must be smaller than or equal to N) * param sizeA Size of A in bytes * param N second value (must be non-zero) * param sizeN Size of N in bytes * param[out] result Output array to store result of operation * param[out] resultSize Output size of operation in bytes * param arithType Type of arithmetic to perform (integer or F2m) * return Operation 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 ((NULL != resultSize) && (NULL != result)) { *resultSize = (uint16_t)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; } /*! * brief Executes Miller-Rabin primality test. * * This function calculates whether or not a candidate prime number is likely * to be a prime. * * param base LTC peripheral base address * param A initial random seed * param sizeA Size of A in bytes * param B number of trial runs * param sizeB Size of B in bytes * param N candidate prime integer * param sizeN Size of N in bytes * param[out] res True if the value is likely prime or false otherwise * return Operation 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; } /*! * brief Adds elliptic curve points - A + B. * * This function performs ECC point addition over a prime field (Fp) or binary field (F2m) using * affine coordinates. * * param base LTC peripheral base address * param A Left-hand point * param B Right-hand point * param N Prime modulus of the field * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from * LTC_PKHA_ModR2() function). * param aCurveParam A parameter from curve equation * param bCurveParam B parameter from curve equation (constant) * param size Size in bytes of curve points and parameters * param arithType Type of arithmetic to perform (integer or F2m) * param[out] result Result point * return Operation 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 != NULL) ? kLTC_PKHA_InputR2 : kLTC_PKHA_CalcR2; clearMask = (uint32_t)kLTC_ClearMode; /* Clear internal register states. */ clearMask |= (uint32_t)kLTC_ClearPkhaSizeA; clearMask |= (uint32_t)kLTC_ClearPkhaSizeB; clearMask |= (uint32_t)kLTC_ClearPkhaSizeN; clearMask |= (uint32_t)kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = (uint32_t)kLTC_StatusDoneIsr; (void)ltc_pkha_clear_regabne(base, true, true, true, false); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKASZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A->X, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 1, A->Y, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 1, B->X, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 2, B->Y, size); if (NULL != R2modN) { (void)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; } /*! * brief Doubles elliptic curve points - B + B. * * This function performs ECC point doubling over a prime field (Fp) or binary field (F2m) using * affine coordinates. * * param base LTC peripheral base address * param B Point to double * param N Prime modulus of the field * param aCurveParam A parameter from curve equation * param bCurveParam B parameter from curve equation (constant) * param size Size in bytes of curve points and parameters * param arithType Type of arithmetic to perform (integer or F2m) * param[out] result Result point * return Operation 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 = (uint32_t)kLTC_ClearMode; /* Clear internal register states. */ clearMask |= (uint32_t)kLTC_ClearPkhaSizeA; clearMask |= (uint32_t)kLTC_ClearPkhaSizeB; clearMask |= (uint32_t)kLTC_ClearPkhaSizeN; clearMask |= (uint32_t)kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = (uint32_t)kLTC_StatusDoneIsr; (void)ltc_pkha_clear_regabne(base, true, true, true, false); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKASZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 1, B->X, size); (void)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; } /*! * brief Multiplies an elliptic curve point by a scalar - E x (A0, A1). * * This function performs ECC point multiplication to multiply an ECC point by * a scalar integer multiplier over a prime field (Fp) or a binary field (F2m). * * param base LTC peripheral base address * param A Point as multiplicand * param E Scalar multiple * param sizeE The size of E, in bytes * param N Modulus, a prime number for the Fp field or Irreducible polynomial for F2m field. * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from * LTC_PKHA_ModR2() function). * param aCurveParam A parameter from curve equation * param bCurveParam B parameter from curve equation (C parameter for operation over F2m). * param size Size in bytes of curve points and parameters * param equalTime Run the function time equalized or no timing equalization. * param arithType Type of arithmetic to perform (integer or F2m) * param[out] result Result point * param[out] infinity Output true if the result is point of infinity, and false otherwise. Writing of this output will * be ignored if the argument is NULL. * return Operation 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 != NULL) ? kLTC_PKHA_InputR2 : kLTC_PKHA_CalcR2; clearMask = (uint32_t)kLTC_ClearMode; /* Clear internal register states. */ clearMask |= (uint32_t)kLTC_ClearPkhaSizeA; clearMask |= (uint32_t)kLTC_ClearPkhaSizeB; clearMask |= (uint32_t)kLTC_ClearPkhaSizeN; clearMask |= (uint32_t)kLTC_ClearPkhaSizeE; base->CW = clearMask; base->STA = (uint32_t)kLTC_StatusDoneIsr; (void)ltc_pkha_clear_regabne(base, true, true, true, true); /* sizeN should be less than 64 bytes. */ base->PKNSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegN, 0, N, size); base->PKESZ = sizeE; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegE, 0, E, sizeE); base->PKASZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 0, A->X, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 1, A->Y, size); (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegA, 3, aCurveParam, size); base->PKBSZ = size; (void)ltc_pkha_write_reg(base, kLTC_PKHA_RegB, 0, bCurveParam, size); if (NULL != R2modN) { (void)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 (NULL != infinity) { *infinity = (bool)(base->STA & (uint32_t)kLTC_StatusPublicKeyOpZero); } } ltc_clear_all(base, true); return status; } #endif /* FSL_FEATURE_LTC_HAS_PKHA */