500 lines
13 KiB
C
500 lines
13 KiB
C
/*
|
|
* Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* @file ck_aes.c
|
|
* @brief CSI Source File for aes driver
|
|
* @version V1.0
|
|
* @date 02. June 2017
|
|
******************************************************************************/
|
|
#include "csi_core.h"
|
|
#include "drv_aes.h"
|
|
#include "ck_aes.h"
|
|
|
|
#define ERR_AES(errno) (CSI_DRV_ERRNO_AES_BASE | errno)
|
|
#define AES_NULL_PARA_CHK(para) \
|
|
do { \
|
|
if (para == NULL) { \
|
|
return ERR_AES(EDRV_PARAMETER); \
|
|
} \
|
|
} while (0)
|
|
static ck_aes_reg_t *aes_reg = NULL;
|
|
|
|
typedef struct {
|
|
uint32_t base;
|
|
uint32_t irq;
|
|
void *iv;
|
|
uint8_t *result_out;
|
|
uint32_t len;
|
|
aes_event_cb_t cb;
|
|
aes_mode_e mode;
|
|
aes_key_len_bits_e keylen;
|
|
aes_endian_mode_e endian;
|
|
aes_status_t status;
|
|
} ck_aes_priv_t;
|
|
|
|
static ck_aes_priv_t aes_handle[CONFIG_AES_NUM];
|
|
|
|
/* Driver Capabilities */
|
|
static const aes_capabilities_t driver_capabilities = {
|
|
.ecb_mode = 1, /* ECB mode */
|
|
.cbc_mode = 1, /* CBC mode */
|
|
.cfb_mode = 0, /* CFB mode */
|
|
.ofb_mode = 0, /* OFB mode */
|
|
.ctr_mode = 0, /* CTR mode */
|
|
.bits_128 = 1, /* 128bits key length mode */
|
|
.bits_192 = 1, /* 192bits key lenght mode */
|
|
.bits_256 = 1 /* 256bits key length mode */
|
|
};
|
|
|
|
//
|
|
// Functions
|
|
//
|
|
|
|
static inline void aes_set_opcode(aes_crypto_mode_e opcode)
|
|
{
|
|
aes_reg->ctrl &= ~(3 << AES_OPCODE_OFFSET); //clear bit[7:6]
|
|
aes_reg->ctrl |= (opcode << AES_OPCODE_OFFSET); //set opcode
|
|
}
|
|
|
|
static inline void aes_set_endian(aes_endian_mode_e endian)
|
|
{
|
|
if (endian == AES_ENDIAN_LITTLE) {
|
|
aes_reg->ctrl &= ~AES_LITTLE_ENDIAN;
|
|
} else {
|
|
aes_reg->ctrl |= AES_LITTLE_ENDIAN;
|
|
}
|
|
}
|
|
|
|
static inline uint32_t aes_set_keylen(aes_key_len_bits_e keylength)
|
|
{
|
|
aes_reg->ctrl &= ~(3 << AES_KEY_LEN_OFFSET); //clear bit[5:4]
|
|
aes_reg->ctrl |= (keylength << AES_KEY_LEN_OFFSET);// Set key length
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void aes_set_mode(aes_mode_e mode)
|
|
{
|
|
aes_reg->ctrl &= ~(1 << AES_MODE_OFFSET); //clear bit 3
|
|
aes_reg->ctrl |= (mode << AES_MODE_OFFSET); //set mode
|
|
}
|
|
|
|
static inline void aes_enable(void)
|
|
{
|
|
aes_reg->ctrl |= (1 << AES_WORK_ENABLE_OFFSET);
|
|
}
|
|
|
|
static inline void aes_disable(void)
|
|
{
|
|
aes_reg->ctrl &= ~(1 << AES_WORK_ENABLE_OFFSET);
|
|
}
|
|
|
|
static inline void aes_enable_interrupt(void)
|
|
{
|
|
aes_reg->ctrl |= (1 << AES_INT_ENABLE_OFFSET);
|
|
}
|
|
|
|
static inline void aes_disable_interrupt(void)
|
|
{
|
|
aes_reg->ctrl &= ~(1 << AES_INT_ENABLE_OFFSET);
|
|
}
|
|
|
|
static inline void aes_clear_interrupt(void)
|
|
{
|
|
aes_reg->state = 0x0;
|
|
}
|
|
|
|
static inline uint32_t aes_get_intstatus(uint32_t AES_IT)
|
|
{
|
|
return (aes_reg->state & AES_IT) ? 1 : 0;
|
|
}
|
|
|
|
static void aes_set_key(void *context, uint8_t *key, uint32_t keylen, uint32_t enc, uint32_t endian)
|
|
{
|
|
uint8_t keynum = 0;
|
|
|
|
if (keylen == AES_KEY_LEN_BITS_128) {
|
|
keynum = 4;
|
|
} else if (keylen == AES_KEY_LEN_BITS_192) {
|
|
keynum = 6;
|
|
} else if (keylen == AES_KEY_LEN_BITS_256) {
|
|
keynum = 8;
|
|
}
|
|
|
|
uint32_t i;
|
|
/* set key according to the endian mode */
|
|
if (endian == AES_ENDIAN_LITTLE) {
|
|
for (i = 0; i < keynum; i++) {
|
|
aes_reg->key[keynum - 1 - i] = *(uint32_t *)key;
|
|
key += 4;
|
|
}
|
|
} else if (endian == AES_ENDIAN_BIG) {
|
|
for (i = 0; i < keynum; i++) {
|
|
aes_reg->key[i] = *(uint32_t *)key;
|
|
key += 4;
|
|
}
|
|
}
|
|
|
|
if (enc == AES_CRYPTO_MODE_DECRYPT) {
|
|
aes_set_opcode(AES_CRYPTO_KEYEXP); /* if the mode is decrypt before decrypt you have to keyexpand */
|
|
aes_enable();
|
|
|
|
while (aes_get_intstatus(AES_IT_KEYINT));
|
|
|
|
aes_set_opcode(AES_CRYPTO_MODE_DECRYPT);
|
|
} else if (enc == AES_CRYPTO_MODE_ENCRYPT) {
|
|
aes_set_opcode(AES_CRYPTO_MODE_ENCRYPT);
|
|
}
|
|
|
|
aes_disable();
|
|
}
|
|
|
|
static int aes_crypto(void *context, uint8_t *in, uint8_t *out,
|
|
uint32_t len, uint8_t *iv, uint32_t mode, uint32_t endian)
|
|
{
|
|
uint32_t i = 0;
|
|
|
|
/* set iv if the mode is CBC */
|
|
if (mode == AES_MODE_CBC) {
|
|
if (endian == AES_ENDIAN_BIG) {
|
|
for (i = 0; i < 4; i++) {
|
|
aes_reg->iv[i] = *(uint32_t *)iv;
|
|
iv += 4;
|
|
}
|
|
} else if (endian == AES_ENDIAN_LITTLE) {
|
|
for (i = 0; i < 4; i++) {
|
|
aes_reg->iv[3 - i] = *(uint32_t *)iv;
|
|
iv += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t j = 0;
|
|
/* set the text before aes calculating */
|
|
for (i = 0; i < len; i = i + 16) {
|
|
for (j = 0; j < 4; j++) {
|
|
if (endian == AES_ENDIAN_BIG) {
|
|
aes_reg->datain[j] = *(uint32_t *)in;
|
|
} else if (endian == AES_ENDIAN_LITTLE) {
|
|
aes_reg->datain[3 - j] = *(uint32_t *)in;
|
|
}
|
|
|
|
in += 4;
|
|
}
|
|
|
|
aes_enable();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ck_aes_irqhandler(int32_t idx)
|
|
{
|
|
ck_aes_priv_t *aes_priv = &aes_handle[idx];
|
|
|
|
volatile uint32_t j;
|
|
uint32_t tmp = 0;
|
|
/* get the result after aes calculating*/
|
|
if (aes_priv->result_out != NULL) {
|
|
for (j = 0; j < 4; j++) {
|
|
if (aes_priv->endian == AES_ENDIAN_BIG) {
|
|
tmp = aes_reg->dataout[j];
|
|
} else if (aes_priv->endian == AES_ENDIAN_LITTLE) {
|
|
tmp = aes_reg->dataout[3 - j];
|
|
}
|
|
|
|
*(uint32_t *)aes_priv->result_out = tmp;
|
|
aes_priv->result_out += 4;
|
|
aes_priv->len -= 4;
|
|
}
|
|
}
|
|
|
|
/* disable aes and clear the aes interrupt */
|
|
aes_disable();
|
|
aes_clear_interrupt();
|
|
|
|
/* execute the callback function */
|
|
if (aes_priv->len == 0) {
|
|
if (aes_priv->cb) {
|
|
aes_priv->cb(AES_EVENT_CRYPTO_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int32_t __attribute__((weak)) target_get_aes_count(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int32_t __attribute__((weak)) target_get_aes(int32_t idx, uint32_t *base, uint32_t *irq)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
\brief get aes instance count.
|
|
\return aes handle count
|
|
*/
|
|
int32_t csi_aes_get_instance_count(void)
|
|
{
|
|
return target_get_aes_count();
|
|
}
|
|
|
|
/**
|
|
\brief Initialize AES Interface. 1. Initializes the resources needed for the AES interface 2.registers event callback function
|
|
\param[in] idx must not exceed return value of csi_aes_get_instance_count().
|
|
\param[in] cb_event Pointer to \ref aes_event_cb_t
|
|
\return return aes handle if success
|
|
*/
|
|
aes_handle_t csi_aes_initialize(int32_t idx, aes_event_cb_t cb_event)
|
|
{
|
|
|
|
if (idx < 0 || idx >= CONFIG_AES_NUM) {
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t irq = 0u;
|
|
uint32_t base = 0u;
|
|
/* obtain the aes information */
|
|
int32_t real_idx = target_get_aes(idx, &base, &irq);
|
|
|
|
if (real_idx != idx) {
|
|
return NULL;
|
|
}
|
|
|
|
ck_aes_priv_t *aes_priv = &aes_handle[idx];
|
|
|
|
aes_priv->base = base;
|
|
aes_priv->irq = irq;
|
|
|
|
/* initialize the aes context */
|
|
aes_reg = (ck_aes_reg_t *)(aes_priv->base);
|
|
aes_priv->cb = cb_event;
|
|
aes_priv->iv = NULL;
|
|
aes_priv->len = 16;
|
|
aes_priv->result_out = NULL;
|
|
aes_priv->mode = AES_MODE_CBC;
|
|
aes_priv->keylen = AES_KEY_LEN_BITS_128;
|
|
aes_priv->endian = AES_ENDIAN_LITTLE;
|
|
aes_priv->status.busy = 0;
|
|
|
|
aes_enable_interrupt(); /* enable the aes interrupt */
|
|
|
|
drv_nvic_enable_irq(aes_priv->irq); /* enable the aes bit in nvic */
|
|
|
|
return (aes_handle_t)aes_priv;
|
|
}
|
|
|
|
/**
|
|
\brief De-initialize AES Interface. stops operation and releases the software resources used by the interface
|
|
\param[in] handle aes handle to operate.
|
|
\return error code
|
|
*/
|
|
int32_t csi_aes_uninitialize(aes_handle_t handle)
|
|
{
|
|
AES_NULL_PARA_CHK(handle);
|
|
|
|
ck_aes_priv_t *aes_priv = handle;
|
|
aes_priv->cb = NULL;
|
|
|
|
aes_disable_interrupt(); /* disable the aes interrupt */
|
|
|
|
drv_nvic_disable_irq(aes_priv->irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
\brief Get driver capabilities.
|
|
\param[in] handle aes handle to operate.
|
|
\return \ref aes_capabilities_t
|
|
*/
|
|
aes_capabilities_t csi_aes_get_capabilities(aes_handle_t handle)
|
|
{
|
|
return driver_capabilities;
|
|
}
|
|
|
|
/**
|
|
\brief config aes mode.
|
|
\param[in] handle aes handle to operate.
|
|
\param[in] mode \ref aes_mode_e
|
|
\param[in] keylen_bits \ref aes_key_len_bits_e
|
|
\param[in] endian \ref aes_endian_mode_e
|
|
\param[in] arg Pointer to the iv address when mode is cbc_mode
|
|
\return error code
|
|
*/
|
|
int32_t csi_aes_config(aes_handle_t handle, aes_mode_e mode, aes_key_len_bits_e keylen_bits, aes_endian_mode_e endian, uint32_t arg)
|
|
{
|
|
AES_NULL_PARA_CHK(handle);
|
|
|
|
ck_aes_priv_t *aes_priv = handle;
|
|
aes_reg = (ck_aes_reg_t *)(aes_priv->base);
|
|
|
|
/* config the aes mode */
|
|
switch (mode) {
|
|
case AES_MODE_CBC:
|
|
aes_priv->iv = (void *)arg;
|
|
aes_priv->mode = mode;
|
|
aes_set_mode(mode);
|
|
break;
|
|
|
|
case AES_MODE_ECB:
|
|
aes_priv->mode = mode;
|
|
aes_set_mode(mode);
|
|
break;
|
|
|
|
case AES_MODE_CFB:
|
|
case AES_MODE_OFB:
|
|
case AES_MODE_CTR:
|
|
return ERR_AES(EDRV_UNSUPPORTED);
|
|
|
|
default:
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
/* config the key length */
|
|
switch (keylen_bits) {
|
|
case AES_KEY_LEN_BITS_128:
|
|
case AES_KEY_LEN_BITS_192:
|
|
case AES_KEY_LEN_BITS_256:
|
|
aes_priv->keylen = keylen_bits;
|
|
aes_set_keylen(keylen_bits);
|
|
break;
|
|
|
|
default:
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
/* config the endian mode */
|
|
switch (endian) {
|
|
case AES_ENDIAN_LITTLE:
|
|
aes_priv->endian = endian;
|
|
aes_set_endian(endian);
|
|
break;
|
|
|
|
case AES_ENDIAN_BIG:
|
|
aes_priv->endian = endian;
|
|
aes_set_endian(endian);
|
|
break;
|
|
|
|
default:
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
\brief set crypto key.
|
|
\param[in] handle aes handle to operate.
|
|
\param[in] context aes information context(NULL when hardware implementation)
|
|
\param[in] key Pointer to the key buf
|
|
\param[in] key_len the key len
|
|
\param[in] enc \ref aes_crypto_mode_e
|
|
\return error code
|
|
*/
|
|
int32_t csi_aes_set_key(aes_handle_t handle, void *context, void *key, uint32_t key_len, aes_crypto_mode_e enc)
|
|
{
|
|
AES_NULL_PARA_CHK(handle);
|
|
AES_NULL_PARA_CHK(key);
|
|
if ((key_len != AES_KEY_LEN_BITS_128 &&
|
|
key_len != AES_KEY_LEN_BITS_192 &&
|
|
key_len != AES_KEY_LEN_BITS_256) ||
|
|
(enc != AES_CRYPTO_MODE_ENCRYPT &&
|
|
enc != AES_CRYPTO_MODE_DECRYPT)) {
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
ck_aes_priv_t *aes_priv = handle;
|
|
aes_set_key(context, key, key_len, enc, aes_priv->endian);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
\brief encrypt or decrypt
|
|
\param[in] handle aes handle to operate.
|
|
\param[in] context aes information context(NULL when hardware implementation)
|
|
\param[in] in Pointer to the Source data
|
|
\param[out] out Pointer to the Result data.
|
|
\param[in] len the Source data len.
|
|
\param[in] padding \ref aes_padding_mode_e.
|
|
\return error code
|
|
*/
|
|
int32_t csi_aes_crypto(aes_handle_t handle, void *context, void *in, void *out, uint32_t len, aes_padding_mode_e padding)
|
|
{
|
|
AES_NULL_PARA_CHK(handle);
|
|
AES_NULL_PARA_CHK(in);
|
|
AES_NULL_PARA_CHK(out);
|
|
AES_NULL_PARA_CHK(len);
|
|
|
|
ck_aes_priv_t *aes_priv = handle;
|
|
|
|
aes_priv->status.busy = 1;
|
|
|
|
uint8_t left_len = len & 0xf;
|
|
switch (padding) {
|
|
case AES_PADDING_MODE_NO:
|
|
if (left_len) {
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
/* crypto in padding no mode */
|
|
aes_priv->result_out = out;
|
|
aes_priv->len = len;
|
|
aes_crypto(context, in, out, len, aes_priv->iv, aes_priv->mode, aes_priv->endian);
|
|
break;
|
|
|
|
case AES_PADDING_MODE_ZERO:
|
|
if (left_len == 0) {
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
uint8_t i = 0;
|
|
for (i = 0; i < (16 - left_len); i++) {
|
|
*((uint8_t *)in + len + i) = 0x0;
|
|
}
|
|
|
|
/* crypto in padding zero mode */
|
|
aes_priv->result_out = out;
|
|
aes_priv->len = len + 16 -left_len;
|
|
aes_crypto(context, in, out, len + 16 - left_len, aes_priv->iv, aes_priv->mode, aes_priv->endian);
|
|
break;
|
|
|
|
case AES_PADDING_MODE_PKCS5:
|
|
return ERR_AES(EDRV_UNSUPPORTED);
|
|
|
|
default:
|
|
return ERR_AES(EDRV_PARAMETER);
|
|
}
|
|
|
|
aes_priv->status.busy = 0;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
\brief Get AES status.
|
|
\param[in] handle aes handle to operate.
|
|
\return AES status \ref aes_status_t
|
|
*/
|
|
aes_status_t csi_aes_get_status(aes_handle_t handle)
|
|
{
|
|
ck_aes_priv_t *aes_priv = handle;
|
|
return aes_priv->status;
|
|
}
|