/* * Original file: * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * All rights reserved. * * Port to Contiki: * Copyright (c) 2013, ADVANSEE - http://www.advansee.com/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * \addtogroup cc2538-aes * @{ * * \file * Implementation of the cc2538 AES driver */ #include "contiki.h" #include "dev/rom-util.h" #include "dev/nvic.h" #include "dev/aes.h" #include "reg.h" #include #include /*---------------------------------------------------------------------------*/ uint8_t aes_load_keys(const void *keys, uint8_t key_size, uint8_t count, uint8_t start_area) { uint32_t aes_key_store_size; uint32_t areas; uint64_t aligned_keys[AES_KEY_AREAS * 128 / 8 / sizeof(uint64_t)]; int i; if(REG(AES_CTRL_ALG_SEL) != 0x00000000) { return CRYPTO_RESOURCE_IN_USE; } /* 192-bit keys must be padded to 256 bits */ if(key_size == AES_KEY_STORE_SIZE_KEY_SIZE_192) { for(i = 0; i < count; i++) { rom_util_memcpy(&aligned_keys[i << 2], &((const uint64_t *)keys)[i * 3], 192 / 8); aligned_keys[(i << 2) + 3] = 0; } } /* Change count to the number of 128-bit key areas */ if(key_size != AES_KEY_STORE_SIZE_KEY_SIZE_128) { count <<= 1; } /* The keys base address needs to be 4-byte aligned */ if(key_size != AES_KEY_STORE_SIZE_KEY_SIZE_192) { rom_util_memcpy(aligned_keys, keys, count << 4); } /* Workaround for AES registers not retained after PM2 */ REG(AES_CTRL_INT_CFG) = AES_CTRL_INT_CFG_LEVEL; REG(AES_CTRL_INT_EN) = AES_CTRL_INT_EN_DMA_IN_DONE | AES_CTRL_INT_EN_RESULT_AV; /* Configure master control module */ REG(AES_CTRL_ALG_SEL) = AES_CTRL_ALG_SEL_KEYSTORE; /* Clear any outstanding events */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_IN_DONE | AES_CTRL_INT_CLR_RESULT_AV; /* Configure key store module (areas, size) * Note that writing AES_KEY_STORE_SIZE deletes all stored keys */ aes_key_store_size = REG(AES_KEY_STORE_SIZE); if((aes_key_store_size & AES_KEY_STORE_SIZE_KEY_SIZE_M) != key_size) { REG(AES_KEY_STORE_SIZE) = (aes_key_store_size & ~AES_KEY_STORE_SIZE_KEY_SIZE_M) | key_size; } /* Free possibly already occupied key areas */ areas = ((0x00000001 << count) - 1) << start_area; REG(AES_KEY_STORE_WRITTEN_AREA) = areas; /* Enable key areas to write */ REG(AES_KEY_STORE_WRITE_AREA) = areas; /* Configure DMAC * Enable DMA channel 0 */ REG(AES_DMAC_CH0_CTRL) = AES_DMAC_CH_CTRL_EN; /* Base address of the keys in ext. memory */ REG(AES_DMAC_CH0_EXTADDR) = (uint32_t)aligned_keys; /* Total keys length in bytes (e.g. 16 for 1 x 128-bit key) */ REG(AES_DMAC_CH0_DMALENGTH) = (REG(AES_DMAC_CH0_DMALENGTH) & ~AES_DMAC_CH_DMALENGTH_DMALEN_M) | (count << (4 + AES_DMAC_CH_DMALENGTH_DMALEN_S)); /* Wait for operation to complete */ while(!(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_RESULT_AV)); /* Check for absence of errors in DMA and key store */ if(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_DMA_BUS_ERR) { REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_BUS_ERR; /* Disable master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; return CRYPTO_DMA_BUS_ERROR; } if(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_KEY_ST_WR_ERR) { REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_KEY_ST_WR_ERR; /* Disable master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; return AES_KEYSTORE_WRITE_ERROR; } /* Acknowledge the interrupt */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_IN_DONE | AES_CTRL_INT_CLR_RESULT_AV; /* Disable master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; /* Check status, if error return error code */ if((REG(AES_KEY_STORE_WRITTEN_AREA) & areas) != areas) { return AES_KEYSTORE_WRITE_ERROR; } return CRYPTO_SUCCESS; } /*---------------------------------------------------------------------------*/ uint8_t aes_auth_crypt_start(uint32_t ctrl, uint8_t key_area, const void *iv, const void *adata, uint16_t adata_len, const void *data_in, void *data_out, uint16_t data_len, struct process *process) { if(REG(AES_CTRL_ALG_SEL) != 0x00000000) { return CRYPTO_RESOURCE_IN_USE; } /* Workaround for AES registers not retained after PM2 */ REG(AES_CTRL_INT_CFG) = AES_CTRL_INT_CFG_LEVEL; REG(AES_CTRL_INT_EN) = AES_CTRL_INT_EN_DMA_IN_DONE | AES_CTRL_INT_EN_RESULT_AV; REG(AES_CTRL_ALG_SEL) = AES_CTRL_ALG_SEL_AES; REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_IN_DONE | AES_CTRL_INT_CLR_RESULT_AV; REG(AES_KEY_STORE_READ_AREA) = key_area; /* Wait until key is loaded to the AES module */ while(REG(AES_KEY_STORE_READ_AREA) & AES_KEY_STORE_READ_AREA_BUSY); /* Check for Key Store read error */ if(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_KEY_ST_RD_ERR) { /* Clear the Keystore Read error bit */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_KEY_ST_RD_ERR; /* Disable the master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; return AES_KEYSTORE_READ_ERROR; } if(iv != NULL) { /* Write initialization vector */ REG(AES_AES_IV_0) = ((const uint32_t *)iv)[0]; REG(AES_AES_IV_1) = ((const uint32_t *)iv)[1]; REG(AES_AES_IV_2) = ((const uint32_t *)iv)[2]; REG(AES_AES_IV_3) = ((const uint32_t *)iv)[3]; } /* Program AES authentication/crypto operation */ REG(AES_AES_CTRL) = ctrl; /* Write the length of the payload block (lo) */ REG(AES_AES_C_LENGTH_0) = data_len; /* Write the length of the payload block (hi) */ REG(AES_AES_C_LENGTH_1) = 0; /* For combined modes only (CCM or GCM) */ if(ctrl & (AES_AES_CTRL_CCM | AES_AES_CTRL_GCM)) { /* Write the length of the AAD data block (may be non-block size-aligned) */ REG(AES_AES_AUTH_LENGTH) = adata_len; if(adata_len != 0) { /* Configure DMAC to fetch the AAD data * Enable DMA channel 0 */ REG(AES_DMAC_CH0_CTRL) = AES_DMAC_CH_CTRL_EN; /* Base address of the AAD data buffer */ REG(AES_DMAC_CH0_EXTADDR) = (uint32_t)adata; /* AAD data length in bytes */ REG(AES_DMAC_CH0_DMALENGTH) = adata_len; /* Wait for completion of the AAD data transfer, DMA_IN_DONE */ while(!(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_DMA_IN_DONE)); /* Check for the absence of error */ if(REG(AES_CTRL_INT_STAT) & AES_CTRL_INT_STAT_DMA_BUS_ERR) { /* Clear the DMA error */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_BUS_ERR; /* Disable the master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; return CRYPTO_DMA_BUS_ERROR; } /* Clear interrupt status */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_IN_DONE; } } /* Enable result available bit in interrupt enable */ REG(AES_CTRL_INT_EN) = AES_CTRL_INT_EN_RESULT_AV; if(process != NULL) { crypto_register_process_notification(process); NVIC_ClearPendingIRQ(AES_IRQn); NVIC_EnableIRQ(AES_IRQn); } if(data_len != 0) { /* Configure DMAC * Enable DMA channel 0 */ REG(AES_DMAC_CH0_CTRL) = AES_DMAC_CH_CTRL_EN; /* Base address of the input payload data buffer */ REG(AES_DMAC_CH0_EXTADDR) = (uint32_t)data_in; /* Input payload data length in bytes */ REG(AES_DMAC_CH0_DMALENGTH) = data_len; if(data_out != NULL) { /* Enable DMA channel 1 */ REG(AES_DMAC_CH1_CTRL) = AES_DMAC_CH_CTRL_EN; /* Base address of the output payload data buffer */ REG(AES_DMAC_CH1_EXTADDR) = (uint32_t)data_out; /* Output payload data length in bytes */ REG(AES_DMAC_CH1_DMALENGTH) = data_len; } } return CRYPTO_SUCCESS; } /*---------------------------------------------------------------------------*/ uint8_t aes_auth_crypt_check_status(void) { return !!(REG(AES_CTRL_INT_STAT) & (AES_CTRL_INT_STAT_DMA_BUS_ERR | AES_CTRL_INT_STAT_KEY_ST_WR_ERR | AES_CTRL_INT_STAT_KEY_ST_RD_ERR | AES_CTRL_INT_STAT_RESULT_AV)); } /*---------------------------------------------------------------------------*/ uint8_t aes_auth_crypt_get_result(void *iv, void *tag) { uint32_t aes_ctrl_int_stat; aes_ctrl_int_stat = REG(AES_CTRL_INT_STAT); /* Clear the error bits */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_BUS_ERR | AES_CTRL_INT_CLR_KEY_ST_WR_ERR | AES_CTRL_INT_CLR_KEY_ST_RD_ERR; NVIC_DisableIRQ(AES_IRQn); crypto_register_process_notification(NULL); /* Disable the master control / DMA clock */ REG(AES_CTRL_ALG_SEL) = 0x00000000; if(aes_ctrl_int_stat & AES_CTRL_INT_STAT_DMA_BUS_ERR) { return CRYPTO_DMA_BUS_ERROR; } if(aes_ctrl_int_stat & AES_CTRL_INT_STAT_KEY_ST_WR_ERR) { return AES_KEYSTORE_WRITE_ERROR; } if(aes_ctrl_int_stat & AES_CTRL_INT_STAT_KEY_ST_RD_ERR) { return AES_KEYSTORE_READ_ERROR; } if(iv != NULL || tag != NULL) { /* Read result * Wait for the context ready bit */ while(!(REG(AES_AES_CTRL) & AES_AES_CTRL_SAVED_CONTEXT_READY)); if(iv != NULL) { /* Read the initialization vector registers */ ((uint32_t *)iv)[0] = REG(AES_AES_IV_0); ((uint32_t *)iv)[1] = REG(AES_AES_IV_1); ((uint32_t *)iv)[2] = REG(AES_AES_IV_2); ((uint32_t *)iv)[3] = REG(AES_AES_IV_3); } if(tag != NULL) { /* Read the tag registers */ ((uint32_t *)tag)[0] = REG(AES_AES_TAG_OUT_0); ((uint32_t *)tag)[1] = REG(AES_AES_TAG_OUT_1); ((uint32_t *)tag)[2] = REG(AES_AES_TAG_OUT_2); ((uint32_t *)tag)[3] = REG(AES_AES_TAG_OUT_3); } } /* Clear the interrupt status */ REG(AES_CTRL_INT_CLR) = AES_CTRL_INT_CLR_DMA_IN_DONE | AES_CTRL_INT_CLR_RESULT_AV; return CRYPTO_SUCCESS; } /** @} */