added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_crypto.c
- Revision:
- 50:a417edff4437
diff -r 57ac6e3cdfd3 -r a417edff4437 targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_crypto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_crypto.c Fri Jan 15 07:45:16 2016 +0000 @@ -0,0 +1,1847 @@ +/***************************************************************************//** + * @file em_crypto.c + * @brief Cryptography accelerator peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ +#include "em_device.h" + +#if defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) + +#include "em_crypto.h" +#include "em_assert.h" +#include "em_bitband.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRYPTO + * + * @brief Cryptography accelerator peripheral API + * + * @details + * This API is intended for use on Silicon Labs target devices, and provides + * a thin software interface for the functions of the crypto module, including + * @li AES (Advanced Encryption Standard) @ref crypto_aes + * @li SHA (Secure Hash Algorithm) @ref crypto_sha + * @li Big Integer multiplier @ref crypto_mul + * @li Functions for loading data and executing instruction sequences @ref crypto_exec + * + * @n @section crypto_aes AES + * The AES APIs include support for AES-128 and AES-256 with block cipher + * modes: + * @li CBC - Cipher Block Chaining mode + * @li CFB - Cipher Feedback mode + * @li CTR - Counter mode + * @li ECB - Electronic Code Book mode + * @li OFB - Output Feedback mode + * + * For the AES APIs Input/output data (plaintext, ciphertext, key etc) are + * treated as byte arrays, starting with most significant byte. Ie, 32 bytes + * of plaintext (B0...B31) is located in memory in the same order, with B0 at + * the lower address and B31 at the higher address. + * + * Byte arrays must always be a multiple of AES block size, ie. a multiple + * of 16. Padding, if required, is done at the end of the byte array. + * + * Byte arrays should be word (32 bit) aligned for performance + * considerations, since the array is accessed with 32 bit access type. + * The core MCUs supports unaligned accesses, but with a performance penalty. + * + * It is possible to specify the same output buffer as input buffer as long + * as they point to the same address. In that case the provided input buffer + * is replaced with the encrypted/decrypted output. Notice that the buffers + * must be exactly overlapping. If partly overlapping, the behavior is + * undefined. + * + * It is up to the user to use a cipher mode according to its requirements + * in order to not break security. Please refer to specific cipher mode + * theory for details. + * + * References: + * @li Wikipedia - Cipher modes, http://en.wikipedia.org/wiki/Cipher_modes + * + * @li Recommendation for Block Cipher Modes of Operation, + * NIST Special Publication 800-38A, 2001 Edition, + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * + * @li Recommendation for Block Cipher Modes of Operation, + * http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + * + * @n @section crypto_sha SHA + * The SHA APIs include support for + * @li SHA-1 @ref CRYPTO_SHA_1 + * @li SHA-256 @ref CRYPTO_SHA_256 + * + * The SHA-1 implementation is FIPS-180-1 compliant, ref: + * @li Wikipedia - SHA-1, https://en.wikipedia.org/wiki/SHA-1 + * @li SHA-1 spec - http://www.itl.nist.gov/fipspubs/fip180-1.htm + * + * The SHA-256 implementation is FIPS-180-2 compliant, ref: + * @li Wikipedia - SHA-2, https://en.wikipedia.org/wiki/SHA-2 + * @li SHA-2 spec - http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * + * @n @section crypto_mul CRYPTO_Mul + * @ref CRYPTO_Mul is a function for multiplying big integers that are + * bigger than the operand size of the MUL instruction which is 128 bits. + * CRYPTO_Mul multiplies all partial operands of the input operands using + * MUL to form a resulting number which may be twice the size of + * the operands. + * + * CRPYTO_Mul is typically used by RSA implementations which perform a + * huge amount of multiplication and square operations in order to + * implement modular exponentiation. + * Some RSA implementations use a number representation including arrays + * of 32bit words of variable size. The user should compile with + * -D USE_VARIABLE_SIZED_DATA_LOADS in order to load these numbers + * directly into CRYPTO without converting the number representation. + * + * @n @section crypto_exec Load and Execute Instruction Sequences + * The functions for loading data and executing instruction sequences can + * be used to implement complex algorithms like elliptic curve cryptography + * (ECC)) and authenticated encryption algorithms. There are two typical + * modes of operation: + * @li Multi sequence operation + * @li Single static instruction sequence operation + * + * In multi sequence mode the software starts by loading input data, then + * an instruction sequence, execute, and finally read the result. This + * process is repeated until the full crypto operation is complete. + * + * When using a single static instruction sequence, there is just one + * instruction sequence which is loaded initially. The sequence can be setup + * to run multiple times. The data can be loaded during the execution of the + * sequence by using DMA, BUFC and/or programmed I/O directly from the MCU + * core. For details on how to program the instruction sequences please refer + * to the reference manual of the particular Silicon Labs device. + * + * In order to load input data to the CRYPTO module use any of the following + * functions: + * @li @ref CRYPTO_DataWrite - Write 128 bits to a DATA register. + * @li @ref CRYPTO_DDataWrite - Write 256 bits to a DDATA register. + * @li @ref CRYPTO_QDataWrite - Write 512 bits to a QDATA register. + * + * In order to read output data from the CRYPTO module use any of the + * following functions: + * @li @ref CRYPTO_DataRead - Read 128 bits from a DATA register. + * @li @ref CRYPTO_DDataRead - Read 256 bits from a DDATA register. + * @li @ref CRYPTO_QDataRead - Read 512 bits from a QDATA register. + * + * In order to load an instruction sequence to the CRYPTO module use + * @ref CRYPTO_InstructionSequenceLoad. + * + * In order to execute the current instruction sequence in the CRYPTO module + * use @ref CRYPTO_InstructionSequenceExecute. + * + * In order to check whether an instruction sequence has completed + * use @ref CRYPTO_InstructionSequenceDone. + * + * In order to wait for an instruction sequence to complete + * use @ref CRYPTO_InstructionSequenceWait. + * + * In order to optimally load (with regards to speed) and execute an + * instruction sequence use any of the CRYPTO_EXECUTE_X macros (where X is + * in the range 1-20) defined in @ref em_crypto.h. E.g. CRYPTO_EXECUTE_19. + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#define CRYPTO_INSTRUCTIONS_PER_REG (4) +#define CRYPTO_INSTRUCTIONS_MAX (12) +#define CRYPTO_INSTRUCTION_REGS (CRYPTO_INSTRUCTIONS_MAX/CRYPTO_INSTRUCTIONS_PER_REG) + +#define CRYPTO_SHA1_BLOCK_SIZE_IN_BITS (512) +#define CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES (CRYPTO_SHA1_BLOCK_SIZE_IN_BITS/8) +#define CRYPTO_SHA1_BLOCK_SIZE_IN_32BIT_WORDS (CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES/sizeof(uint32_t)) +#define CRYPTO_SHA1_DIGEST_SIZE_IN_32BIT_WORDS (CRYPTO_SHA1_DIGEST_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_SHA256_BLOCK_SIZE_IN_BITS (512) +#define CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES (CRYPTO_SHA256_BLOCK_SIZE_IN_BITS/8) +#define CRYPTO_SHA256_BLOCK_SIZE_IN_32BIT_WORDS (CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_SHA256_DIGEST_SIZE_IN_32BIT_WORDS (CRYPTO_SHA256_DIGEST_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define PARTIAL_OPERAND_WIDTH_LOG2 (7) /* 2^7 = 128 */ +#define PARTIAL_OPERAND_WIDTH (1<<PARTIAL_OPERAND_WIDTH_LOG2) +#define PARTIAL_OPERAND_WIDTH_MASK (PARTIAL_OPERAND_WIDTH-1) +#define PARTIAL_OPERAND_WIDTH_IN_BYTES (PARTIAL_OPERAND_WIDTH/8) +#define PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS (PARTIAL_OPERAND_WIDTH_IN_BYTES/sizeof(uint32_t)) + +#define SWAP32(x) (__REV(x)) + +#define CRYPTO_AES_BLOCKSIZE (16) + +/******************************************************************************* + *********************** STATIC FUNCTIONS ********************************** + ******************************************************************************/ + +static inline void CRYPTO_AES_ProcessLoop(uint32_t len, + CRYPTO_DataReg_TypeDef inReg, + uint32_t * in, + CRYPTO_DataReg_TypeDef outReg, + uint32_t * out); + +static void CRYPTO_AES_CBCx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth); + +static void CRYPTO_AES_CFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth); + +static void CRYPTO_AES_CTRx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc, + CRYPTO_KeyWidth_TypeDef keyWidth); + +static void CRYPTO_AES_ECBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth); + +static void CRYPTO_AES_OFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + CRYPTO_KeyWidth_TypeDef keyWidth); + +#ifdef USE_VARIABLE_SIZED_DATA_LOADS +/***************************************************************************//** + * @brief + * Write variable sized 32 bit data array (max 128 bits) to a DATAX register + * + * @details + * Write variable sized 32 bit array (max 128 bits / 4 words) to a DATAX + * register in the CRYPTO module. + * + * @param[in] dataReg The 128 bits DATA register. + * @param[in] val Value of the data to write to the DATA register. + * @param[in] valSize Size of @ref val in number of 32bit words. + ******************************************************************************/ +__STATIC_INLINE +void CRYPTO_DataWriteVariableSize(CRYPTO_DataReg_TypeDef dataReg, + const CRYPTO_Data_TypeDef val, + int valSize) +{ + int i; + volatile uint32_t * reg = (volatile uint32_t *) dataReg; + + if (valSize < 4) + { + /* Non optimal write of data. */ + for (i = 0; i < valSize; i++) + *reg = *val++; + for (; i < 4; i++) + *reg = 0; + } + else + { + CRYPTO_BurstToCrypto(reg, &val[0]); + } +} +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Set the modulus type used for wide arithmetic operations. + * + * @details + * This function sets the modulus type to be used by the Modulus instructions + * of the CRYPTO module. + * + * @param[in] modType Modulus type. + ******************************************************************************/ +void CRYPTO_ModulusSet(CRYPTO_ModulusType_TypeDef modType) +{ + uint32_t temp = CRYPTO->WAC & (~(_CRYPTO_WAC_MODULUS_MASK | _CRYPTO_WAC_MODOP_MASK)); + + switch (modType) + { + case cryptoModulusBin256: + case cryptoModulusBin128: + case cryptoModulusGcmBin128: + case cryptoModulusEccB233: + case cryptoModulusEccB163: +#ifdef _CRYPTO_WAC_MODULUS_ECCBIN233N + case cryptoModulusEccB233Order: + case cryptoModulusEccB233KOrder: + case cryptoModulusEccB163Order: + case cryptoModulusEccB163KOrder: +#endif + CRYPTO->WAC = temp | modType | CRYPTO_WAC_MODOP_BINARY; + break; + + case cryptoModulusEccP256: + case cryptoModulusEccP224: + case cryptoModulusEccP192: +#ifdef _CRYPTO_WAC_MODULUS_ECCPRIME256P + case cryptoModulusEccP256Order: + case cryptoModulusEccP224Order: + case cryptoModulusEccP192Order: +#endif + CRYPTO->WAC = temp | modType | CRYPTO_WAC_MODOP_REGULAR; + break; + + default: + /* Unknown modulus type. */ + EFM_ASSERT(0); + } +} + +/***************************************************************************//** + * @brief + * Read the key value currently used by the CRYPTO module. + * + * @details + * Read 128 bits or 256 bits from KEY register in the CRYPTO module. + * + * @param[in] val Value of the data to write to the KEYBUF register. + * @param[in] keyWidth Key width - 128 or 256 bits + ******************************************************************************/ +void CRYPTO_KeyRead(CRYPTO_KeyBuf_TypeDef val, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(val); + + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &val[0]); + if (keyWidth == cryptoKey256Bits) + { + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &val[4]); + } +} + +/***************************************************************************//** + * @brief + * Perform a SHA-1 hash operation on a message. + * + * @details + * This function performs a SHA-1 hash operation on the message specified by + * msg with length msgLen, and returns the message digest in msgDigest. + * + * @param[in] msg Message to hash. + * @param[in] msgLen Length of message in bytes. + * @param[out] msgDigest Message digest. + ******************************************************************************/ +void CRYPTO_SHA_1(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA1_Digest_TypeDef msgDigest) +{ + uint32_t temp; + int len; + int blockLen; + uint32_t shaBlock[CRYPTO_SHA1_BLOCK_SIZE_IN_32BIT_WORDS]= + { + /* Initial value */ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 + }; + uint8_t * p8ShaBlock = (uint8_t *) shaBlock; + + /* Initialize crypto module to do SHA-1. */ + CRYPTO->CTRL = CRYPTO_CTRL_SHA_SHA1; + CRYPTO->SEQCTRL = 0; + CRYPTO->SEQCTRLB = 0; + + /* Set result width of MADD32 operation. */ + CRYPTO_ResultWidthSet(cryptoResult256Bits); + + /* Write init value to DDATA1. */ + CRYPTO_DDataWrite(cryptoRegDDATA1, shaBlock); + + /* Copy data to DDATA0 and select DDATA0 and DDATA1 for SHA operation. */ + CRYPTO_EXECUTE_2(CRYPTO_CMD_INSTR_DDATA1TODDATA0, + CRYPTO_CMD_INSTR_SELDDATA0DDATA1); + + len = msgLen; + + while (len >= CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES) + { + /* Write block to QDATA1. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, (uint32_t *) msg); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + len -= CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES; + msg += CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES; + } + + blockLen = 0; + + /* Build the last (or second to last) block */ + for (; len; len--) + p8ShaBlock[blockLen++] = *msg++; + + /* append the '1' bit */ + p8ShaBlock[blockLen++] = 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (blockLen > 56) + { + while (blockLen < 64) + p8ShaBlock[blockLen++] = 0; + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + blockLen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (blockLen < 56) + p8ShaBlock[blockLen++] = 0; + + /* And finally, encode the message length. */ + { + uint64_t msgLenInBits = msgLen << 3; + temp = msgLenInBits >> 32; + *(uint32_t*)&p8ShaBlock[56] = SWAP32(temp); + temp = msgLenInBits & 0xFFFFFFFF; + *(uint32_t*)&p8ShaBlock[60] = SWAP32(temp); + } + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + /* Read resulting message digest from DDATA0BIG. */ + ((uint32_t*)msgDigest)[0] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[1] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[2] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[3] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[4] = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; +} + +/***************************************************************************//** + * @brief + * Perform a SHA-256 hash operation on a message. + * + * @details + * This function performs a SHA-256 hash operation on the message specified + * by msg with length msgLen, and returns the message digest in msgDigest. + * + * @param[in] msg Message to hash. + * @param[in] msgLen Length of message in bytes. + * @param[out] msgDigest Message digest. + ******************************************************************************/ +void CRYPTO_SHA_256(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA256_Digest_TypeDef msgDigest) +{ + uint32_t temp; + int len; + int blockLen; + uint32_t shaBlock[CRYPTO_SHA256_BLOCK_SIZE_IN_32BIT_WORDS]= + { + /* Initial value */ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + uint8_t * p8ShaBlock = (uint8_t *) shaBlock; + + /* Initialize crypyo module to do SHA-256 (SHA-2). */ + CRYPTO->CTRL = CRYPTO_CTRL_SHA_SHA2; + CRYPTO->SEQCTRL = 0; + CRYPTO->SEQCTRLB = 0; + + /* Set result width of MADD32 operation. */ + CRYPTO_ResultWidthSet(cryptoResult256Bits); + + /* Write init value to DDATA1. */ + CRYPTO_DDataWrite(cryptoRegDDATA1, shaBlock); + + /* Copy data ot DDATA0 and select DDATA0 and DDATA1 for SHA operation. */ + CRYPTO_EXECUTE_2(CRYPTO_CMD_INSTR_DDATA1TODDATA0, + CRYPTO_CMD_INSTR_SELDDATA0DDATA1); + len = msgLen; + + while (len >= CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES) + { + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, (uint32_t *) msg); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + len -= CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES; + msg += CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES; + } + + blockLen = 0; + + /* Build the last (or second to last) block */ + for (; len; len--) + p8ShaBlock[blockLen++] = *msg++; + + /* append the '1' bit */ + p8ShaBlock[blockLen++] = 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (blockLen > 56) + { + while (blockLen < 64) + p8ShaBlock[blockLen++] = 0; + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + blockLen = 0; + } + + /* Pad upto 56 bytes of zeroes */ + while (blockLen < 56) + p8ShaBlock[blockLen++] = 0; + + /* And finally, encode the message length. */ + { + uint64_t msgLenInBits = msgLen << 3; + temp = msgLenInBits >> 32; + *(uint32_t *)&p8ShaBlock[56] = SWAP32(temp); + temp = msgLenInBits & 0xFFFFFFFF; + *(uint32_t *)&p8ShaBlock[60] = SWAP32(temp); + } + + /* Write the final block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + /* Read resulting message digest from DDATA0BIG. */ + CRYPTO_DDataRead(cryptoRegDDATA0BIG, (uint32_t *)msgDigest); +} + +/***************************************************************************//** + * @brief + * Set 32bit word array to zero. + * + * @param[in] words32bits Pointer to 32bit word array + * @param[in] num32bitWords Number of 32bit words in array + ******************************************************************************/ +__STATIC_INLINE void cryptoBigintZeroize(uint32_t * words32bits, + int num32bitWords) +{ + while (num32bitWords--) + *words32bits++ = 0; +} + +/***************************************************************************//** + * @brief + * Increment value of 32bit word array by one. + * + * @param[in] words32bits Pointer to 32bit word array + * @param[in] num32bitWords Number of 32bit words in array + ******************************************************************************/ +__STATIC_INLINE void cryptoBigintIncrement(uint32_t * words32bits, + int num32bitWords) +{ + int i; + for (i=0; i<num32bitWords; i++) + if (++words32bits[i] != 0) + break; + return; +} + +/***************************************************************************//** + * @brief + * Multiply two big integers. + * + * @details + * This function uses the CRYPTO unit to multiply two big integer operands. + * If USE_VARIABLE_SIZED_DATA_LOADS is defined, the sizes of the operands + * may be any multiple of 32 bits. If USE_VARIABLE_SIZED_DATA_LOADS is _not_ + * defined, the sizes of the operands must be a multiple of 128 bits. + * + * @param[in] A operand A + * @param[in] aSize size of operand A in bits + * @param[in] B operand B + * @param[in] bSize size of operand B in bits + * @param[out] R result of multiplication + * @param[in] rSize size of result buffer R in bits + ******************************************************************************/ +void CRYPTO_Mul(uint32_t * A, int aSize, + uint32_t * B, int bSize, + uint32_t * R, int rSize) +{ + int i, j; + + /**************** Initializations ******************/ + +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + int numWordsLastOperandA = (aSize&PARTIAL_OPERAND_WIDTH_MASK)>>5; + int numPartialOperandsA = numWordsLastOperandA ? + (aSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + aSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numWordsLastOperandB = (bSize&PARTIAL_OPERAND_WIDTH_MASK)>>5; + int numPartialOperandsB = numWordsLastOperandB ? + (bSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + bSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numWordsLastOperandR = (rSize&PARTIAL_OPERAND_WIDTH_MASK)>>5; + int numPartialOperandsR = numWordsLastOperandR ? + (rSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + rSize >> PARTIAL_OPERAND_WIDTH_LOG2; + EFM_ASSERT(numPartialOperandsA + numPartialOperandsB <= numPartialOperandsR); +#else + int numPartialOperandsA = aSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numPartialOperandsB = bSize >> PARTIAL_OPERAND_WIDTH_LOG2; + EFM_ASSERT((aSize & PARTIAL_OPERAND_WIDTH_MASK) == 0); + EFM_ASSERT((bSize & PARTIAL_OPERAND_WIDTH_MASK) == 0); +#endif + EFM_ASSERT(aSize + bSize <= rSize); + + /* Set R to zero. */ + cryptoBigintZeroize(R, rSize >> 5); + + /* Set multiplication width. */ + CRYPTO->WAC = CRYPTO_WAC_MULWIDTH_MUL128 | CRYPTO_WAC_RESULTWIDTH_256BIT; + + /* Setup DMA request signalling in order for MCU to run in parallel with + CRYPTO instruction sequence execution, and prepare data loading which + can take place immediately when CRYPTO is ready inside the instruction + sequence. */ + CRYPTO->CTRL = + CRYPTO_CTRL_DMA0RSEL_DATA0 | CRYPTO_CTRL_DMA0MODE_FULL | + CRYPTO_CTRL_DMA1RSEL_DATA1 | CRYPTO_CTRL_DMA1MODE_FULL; + + CRYPTO_EXECUTE_4( + CRYPTO_CMD_INSTR_CCLR, /* Carry = 0 */ + CRYPTO_CMD_INSTR_CLR, /* DDATA0 = 0 */ + /* clear result accumulation register */ + CRYPTO_CMD_INSTR_DDATA0TODDATA2, + CRYPTO_CMD_INSTR_SELDDATA1DDATA3); + /* + register map: + DDATA0: working register + DDATA1: B(j) + DDATA2: R(i+j+1) and R(i+j), combined with DMA entry for B(j) + DDATA3: A(i) + */ + + CRYPTO_SEQ_LOAD_10( + /* Temporarily load partial operand B(j) to DATA0. */ + /* R(i+j+1) is still in DATA1 */ + CRYPTO_CMD_INSTR_DMA0TODATA, + /* Move B(j) to DDATA1 */ + CRYPTO_CMD_INSTR_DDATA2TODDATA1, + + /* Restore previous partial result (now R(i+j)) */ + CRYPTO_CMD_INSTR_DATA1TODATA0, + + /* Load next partial result R(i+j+1) */ + CRYPTO_CMD_INSTR_DMA1TODATA, + + /* Execute partial multiplication A(i)inDDATA1 * B(j)inDDATA3*/ + CRYPTO_CMD_INSTR_MULO, + + /* Add the result to the previous partial result */ + /* AND take the previous carry value into account */ + /* at the right place (bit 128, ADDIC instruction */ + CRYPTO_CMD_INSTR_SELDDATA0DDATA2, + CRYPTO_CMD_INSTR_ADDIC, + + /* Save the new partial result (lower half) */ + CRYPTO_CMD_INSTR_DDATA0TODDATA2, + CRYPTO_CMD_INSTR_DATATODMA0, + /* Reset the operand selector for next*/ + CRYPTO_CMD_INSTR_SELDDATA2DDATA3 + ); + + /**************** End Initializations ******************/ + + for(i=0; i<numPartialOperandsA; i++) + { + /* Load partial operand #1 A>>(i*PARTIAL_OPERAND_WIDTH) to DDATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandA != 0) && ( i == numPartialOperandsA-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA2, + &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandA); + else + CRYPTO_DataWrite(cryptoRegDATA2, &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA2, &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Load partial result in R>>(i*PARTIAL_OPERAND_WIDTH) to DATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandR != 0) && ( i == numPartialOperandsR-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA1, + &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandR); + else + CRYPTO_DataWrite(cryptoRegDATA1, &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA1, &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Clear carry */ + CRYPTO->CMD = CRYPTO_CMD_INSTR_CCLR; + + /* Setup number of sequence iterations and block size. */ + CRYPTO->SEQCTRL = CRYPTO_SEQCTRL_BLOCKSIZE_16BYTES + | (PARTIAL_OPERAND_WIDTH_IN_BYTES * numPartialOperandsB); + + /* Execute the MULtiply instruction sequence. */ + CRYPTO_InstructionSequenceExecute(); + + for (j=0; j<numPartialOperandsB; j++) + { + /* Load partial operand 2 B>>(j*`PARTIAL_OPERAND_WIDTH) to DDATA2 + (via DATA0). */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandB != 0) && ( j == numPartialOperandsB-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandB); + else + CRYPTO_DataWrite(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Load most significant partial result + R>>((i+j+1)*`PARTIAL_OPERAND_WIDTH) into DATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandR != 0) && ( (i+j+1) == numPartialOperandsR-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandR); + else + CRYPTO_DataWrite(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + /* Store least significant partial result */ + CRYPTO_DataRead(cryptoRegDATA0, + &R[(i+j)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); + + } /* for (j=0; j<numPartialOperandsB; j++) */ + + /* Handle carry at the end of the inner loop. */ + if (CRYPTO_CarryIsSet()) + cryptoBigintIncrement(&R[(i+numPartialOperandsB+1) + *PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + (numPartialOperandsA-i-1) + *PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS); + + CRYPTO_DataRead(cryptoRegDATA1, + &R[(i+numPartialOperandsB) + * PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); + + } /* for (i=0; i<numPartialOperandsA; i++) */ +} + +/***************************************************************************//** + * @brief + * AES Cipher-block chaining (CBC) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Plaintext Plaintext + * | | + * V V + * InitVector ->XOR +-------------->XOR + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * |----------+ | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | decryption | | | decryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * InitVector ->XOR +-------------->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey128(). + * If this argument is null, the key will not be loaded, as it is assumed + * the key has been loaded into KEYHA previously. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CBC128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CBCx(out, in, len, key, iv, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher-block chaining (CBC) cipher mode encryption/decryption, 256 bit + * key. + * + * @details + * Please see CRYPTO_AES_CBC128() for CBC figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CBC256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CBCx(out, in, len, key, iv, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * Plaintext ->XOR | Plaintext ->XOR + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * XOR<- Ciphertext XOR<- Ciphertext + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CFBx(out, in, len, key, iv, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_CFB128() for CFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CFBx(out, in, len, key, iv, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * If this argument is null, the key will not be loaded, as it is assumed + * the key has been loaded into KEYHA previously. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + ******************************************************************************/ +void CRYPTO_AES_CTR128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CTRx(out, in, len, key, ctr, ctrFunc, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_CTR128() for CTR figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + ******************************************************************************/ +void CRYPTO_AES_CTR256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CTRx(out, in, len, key, ctr, ctrFunc, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * Update last 32 bits of 128 bit counter, by incrementing with 1. + * + * @details + * Notice that no special consideration is given to possible wrap around. If + * 32 least significant bits are 0xFFFFFFFF, they will be updated to 0x00000000, + * ignoring overflow. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[in,out] ctr + * Buffer holding 128 bit counter to be updated. + ******************************************************************************/ +void CRYPTO_AES_CTRUpdate32Bit(uint8_t * ctr) +{ + uint32_t * _ctr = (uint32_t *) ctr; + + _ctr[3] = __REV(__REV(_ctr[3]) + 1); +} + +/***************************************************************************//** + * @brief + * Generate 128 bit AES decryption key from 128 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 128 bit decryption key. Must be at least 16 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 128 bit encryption key. Must be at least 16 bytes long. + ******************************************************************************/ +void CRYPTO_AES_DecryptKey128(uint8_t * out, const uint8_t * in) +{ + uint32_t * _out = (uint32_t *) out; + const uint32_t * _in = (const uint32_t *) in; + + /* Load key */ + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[0]); + + /* Do dummy encryption to generate decrypt key */ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_IntClear(CRYPTO_IF_INSTRDONE); + CRYPTO->CMD = CRYPTO_CMD_INSTR_AESENC; + + /* Save decryption key */ + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[0]); +} + +/***************************************************************************//** + * @brief + * Generate 256 bit AES decryption key from 256 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 256 bit decryption key. Must be at least 32 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 256 bit encryption key. Must be at least 32 bytes long. + ******************************************************************************/ +void CRYPTO_AES_DecryptKey256(uint8_t * out, const uint8_t * in) +{ + uint32_t * _out = (uint32_t *) out; + const uint32_t * _in = (const uint32_t *) in; + + /* Load key */ + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[0]); + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[4]); + + /* Do dummy encryption to generate decrypt key */ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO->CMD = CRYPTO_CMD_INSTR_AESENC; + + /* Save decryption key */ + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[0]); + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[4]); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Plaintext Plaintext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | decryption | | decryption | + * +--------------+ +--------------+ + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey128(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_ECB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_ECBx(out, in, len, key, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 256 bit key. + * + * @details + * Please see CRYPTO_AES_ECB128() for ECB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_ECB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_ECBx(out, in, len, key, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + ******************************************************************************/ +void CRYPTO_AES_OFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_OFBx(out, in, len, key, iv, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_OFB128() for OFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + ******************************************************************************/ +void CRYPTO_AES_OFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_OFBx(out, in, len, key, iv, cryptoKey256Bits); +} + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Cipher-block chaining (CBC) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CBC128() for CBC figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CBCx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + if (encrypt) + { + CRYPTO_DataWrite(cryptoRegDATA0, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + } + else + { + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *) iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA1TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESDEC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA2TODATA0XOR << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + + CRYPTO->SEQ1 = 0; + + /* The following call is equivalent to the last call in the + 'if( encrypt )' branch. However moving this + call outside the conditional scope results in slightly poorer + performance for some compiler optimizations. */ + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + } +} + +/***************************************************************************//** + * @brief + * Cipher feedback (CFB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CFB128() for CFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->WAC = 0; + + /* Load Key */ + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + /* Load instructions to CRYPTO sequencer. */ + if (encrypt) + { + /* Load IV */ + CRYPTO_DataWrite(cryptoRegDATA0, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR1_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *)in, + cryptoRegDATA0, (uint32_t *)out + ); + } + else + { + /* Load IV */ + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA2TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + CRYPTO->SEQ1 = 0; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *)in, + cryptoRegDATA0, (uint32_t *)out + ); + } +} + +/***************************************************************************//** + * @brief + * Counter (CTR) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CTR128() for CTR figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CTRx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + (void) ctrFunc; + + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->CTRL |= CRYPTO_CTRL_INCWIDTH_INCWIDTH4; + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + CRYPTO_DataWrite(cryptoRegDATA1, (uint32_t *) ctr); + + CRYPTO->SEQ0 = CRYPTO_CMD_INSTR_DATA1TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA3 << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1INC << _CRYPTO_SEQ0_INSTR3_SHIFT; + + CRYPTO->SEQ1 = CRYPTO_CMD_INSTR_DATA2TODATA0XOR << _CRYPTO_SEQ1_INSTR4_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA2, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + + CRYPTO_DataRead(cryptoRegDATA1, (uint32_t *) ctr); +} + +/***************************************************************************//** + * @brief + * Electronic Codebook (ECB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_ECB128() for ECB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_ECBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + if (encrypt) + { + CRYPTO->SEQ0 = + (CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR1_SHIFT); + } + else + { + CRYPTO->SEQ0 = + (CRYPTO_CMD_INSTR_AESDEC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR1_SHIFT); + } + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA0, (uint32_t *) in, + cryptoRegDATA1, (uint32_t *) out); +} + +/***************************************************************************//** + * @brief + * Output feedback (OFB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_OFB128() for OFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_OFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA2TODATA0 << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + CRYPTO->SEQ1 = + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ1_INSTR4_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ1_INSTR5_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA0, (uint32_t *) in, + cryptoRegDATA1, (uint32_t *) out); +} + +/***************************************************************************//** + * @brief + * Function performs generic AES loop. + * + * @details + * Function loads given register with provided input data. Triggers CRYPTO to + * perform sequence of instructions and read specified output register to + * output buffer. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] inReg + * Input register - one of DATA0,DATA1,DATA2,DATA3 + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] outReg + * Output register - one of DATA0,DATA1,DATA2,DATA3 + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + ******************************************************************************/ +static inline void CRYPTO_AES_ProcessLoop(uint32_t len, + CRYPTO_DataReg_TypeDef inReg, + uint32_t * in, + CRYPTO_DataReg_TypeDef outReg, + uint32_t * out) +{ + len /= CRYPTO_AES_BLOCKSIZE; + CRYPTO->SEQCTRL = 16 << _CRYPTO_SEQCTRL_LENGTHA_SHIFT; + + while (len--) + { + /* Load data and trigger encryption */ + CRYPTO_DataWrite(inReg, (uint32_t *)in); + + CRYPTO->CMD = CRYPTO_CMD_SEQSTART; + + /* Save encrypted/decrypted data */ + CRYPTO_DataRead(outReg, (uint32_t *)out); + + out += 4; + in += 4; + } +} + +/** @} (end addtogroup CRYPTO) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) */