/** * @file ske_basic.c * @brief Semidrive CRYPTO ske basic source file. * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. */ #include #include "ske_basic.h" #include "sdrv_crypto_utility.h" #include "trng.h" /* ske_hp register pointer */ volatile static ske_hp_reg_t *const g_ske_hp_reg = (ske_hp_reg_t *)(SKE_HP_BASE_ADDR); /* function: get ske IP version * parameters: none * return: ske IP version * caution: */ uint32_t ske_get_version(void) { return g_ske_hp_reg->ske_version; } /* function: set ske_hp to be CPU mode * parameters: none * return: none * caution: */ void ske_hp_set_cpu_mode(void) { g_ske_hp_reg->cfg &= (~((0x1) << SKE_HP_DMA_OFFSET)); } /* function: set ske_hp to be DMA mode * parameters: none * return: none * caution: */ void ske_hp_set_dma_mode(void) { g_ske_hp_reg->cfg |= (0x1) << SKE_HP_DMA_OFFSET; } /* function: enable ske_hp DMA linked list function * parameters: none * return: none * caution: * 1. this works when DMA mode is enabled */ void ske_hp_enable_dma_linked_list(void) { g_ske_hp_reg->cfg |= (0x1) << SKE_HP_DMA_LL_OFFSET; } /* function: disable ske_hp DMA linked list function * parameters: none * return: none * caution: */ void ske_hp_disable_dma_linked_list(void) { g_ske_hp_reg->cfg &= ~((0x1) << SKE_HP_DMA_LL_OFFSET); } /* function: set the ske_hp endian * parameters: none * return: none * caution: * 1. actually, this config works for only CPU mode now */ void ske_hp_set_endian_uint32(void) { /* clear bit[25:24], and now CPU is big-endian */ g_ske_hp_reg->cfg &= ~(((uint32_t)3) << SKE_HP_REVERSE_BYTE_ORDER_IN_WORD_OFFSET); /* CPU is little-endian, input and output reversed by hardware----ske IP */ #ifndef SKE_HP_CPU_BIG_ENDIAN g_ske_hp_reg->cfg |= (((uint32_t)2) << SKE_HP_REVERSE_BYTE_ORDER_IN_WORD_OFFSET); #endif } /* function: enable ske_hp secure port * parameters: * sp_key_idx ----------------- input, index of secure port key, (sp_key_idx * & 0x7FFF) must be in [1,MAX_KEY_IDX], if the MSB(sp_key_idx) is 1, that means * using low 128bit of the 256bit key return: none caution: */ void ske_hp_enable_secure_port(uint16_t sp_key_idx) { #ifdef SKE_SECURE_PORT_FUNCTION g_ske_hp_reg->sp |= 0x1; OTP_KEY_CTRL = 0x0; /* use the low 128bit of the 256bit key */ if (sp_key_idx & 0x8000) { OTP_KEY_CTRL = (0x1 << 24); } /* set key idx */ if (sp_key_idx == 9) { OTP_KEY_CTRL |= 0x100000; } else { sp_key_idx -= 1; OTP_KEY_CTRL |= ((uint32_t)(sp_key_idx & 0x7FFF)) << 16; } OTP_KEY_CTRL |= 0x1; #endif } /* function: disable ske_hp secure port * parameters: none * return: none * caution: */ void ske_hp_disable_secure_port(void) { g_ske_hp_reg->sp &= ~(0x1); } /* function: set ske_hp encrypting or decrypting * parameters: * crypto --------------------- input, SKE_CRYPTO_ENCRYPT or * SKE_CRYPTO_DECRYPT return: none caution: * 1. please make sure crypto is valid */ void ske_hp_set_crypto(SKE_CRYPTO crypto) { g_ske_hp_reg->cfg &= ~(((uint32_t)1) << SKE_HP_CRYPTO_OFFSET); g_ske_hp_reg->cfg |= (((uint32_t)crypto) << SKE_HP_CRYPTO_OFFSET); } /* function: set ske_hp alg * parameters: * ske_alg -------------------- input, ske_hp algorithm * return: none * caution: * 1. please make sure ske_alg is valid */ void ske_hp_set_alg(SKE_ALG ske_alg) { uint32_t cfg; switch (ske_alg) { #ifdef SUPPORT_SKE_DES case SKE_ALG_DES: cfg = 3; break; #endif #ifdef SUPPORT_SKE_TDES_128 case SKE_ALG_TDES_128: #endif #ifdef SUPPORT_SKE_TDES_192 case SKE_ALG_TDES_192: #endif #if (defined(SUPPORT_SKE_TDES_128) || defined(SUPPORT_SKE_TDES_192)) cfg = 4; break; #endif #ifdef SUPPORT_SKE_TDES_EEE_128 case SKE_ALG_TDES_EEE_128: #endif #ifdef SUPPORT_SKE_TDES_EEE_192 case SKE_ALG_TDES_EEE_192: #endif #if (defined(SUPPORT_SKE_TDES_EEE_128) || defined(SUPPORT_SKE_TDES_EEE_192)) cfg = 5; break; #endif #ifdef SUPPORT_SKE_AES_128 case SKE_ALG_AES_128: cfg = (1 << 6) | (1 << 4) | (1); break; #endif #ifdef SUPPORT_SKE_AES_192 case SKE_ALG_AES_192: cfg = (2 << 6) | (2 << 4) | (1); break; #endif #ifdef SUPPORT_SKE_AES_256 case SKE_ALG_AES_256: cfg = (3 << 6) | (3 << 4) | (1); break; #endif #ifdef SUPPORT_SKE_SM4 case SKE_ALG_SM4: cfg = 2; break; #endif default: cfg = 2; } g_ske_hp_reg->cfg &= (~(0x000000FFU)); g_ske_hp_reg->cfg |= cfg; } /* function: set ske_hp alg operation mode * parameters: * mode ----------------------- input, operation mode * return: none * caution: * 1. please make sure mode is valid */ void ske_hp_set_mode(SKE_MODE mode) { g_ske_hp_reg->cfg &= (~(0x0000000F << SKE_HP_MODE_OFFSET)); g_ske_hp_reg->cfg |= (((uint32_t)mode) << SKE_HP_MODE_OFFSET); } /* function: set whether ske_hp current input data is the last data or not * parameters: * is_last_block -------------- input, 0:no, other:yes * return: none * caution: * 1. just for CMAC/CCM/GCM/XTS mode */ void ske_hp_set_last_block(uint32_t is_last_block) { if (is_last_block) { g_ske_hp_reg->m_din_cr |= (((uint32_t)1) << SKE_HP_LAST_DATA_OFFSET); } else { g_ske_hp_reg->m_din_cr &= ~(((uint32_t)1) << SKE_HP_LAST_DATA_OFFSET); } } /* function: set ske_hp current input data bit length * parameters: * bytes ---------------------- input, byte length of current input data * return: none * caution: * 1. just for CMAC */ void ske_hp_set_last_block_len(uint32_t bytes) { g_ske_hp_reg->m_din_cr &= ~0x000000FF; g_ske_hp_reg->m_din_cr |= (bytes << 3); } /* function: set ske_hp seed * parameters: none * return: SKE_SUCCESS(success), other(error) * caution: * 1. */ uint32_t ske_hp_set_seed(void) { if (TRNG_SUCCESS != get_rand((uint8_t *)(g_ske_hp_reg->ske_seed), sizeof(g_ske_hp_reg->ske_seed))) { return SKE_ERROR; } else { return SKE_SUCCESS; } } /* function: ske_hp start to expand key or calc * parameters:none * return: none * caution: */ void ske_hp_start(void) { g_ske_hp_reg->risr = 0x0; g_ske_hp_reg->ctrl |= 0x1; } uint32_t ske_hp_check_attack_state(void) { if (g_ske_hp_reg->ske_alarm & 0x1) { return SKE_ATTACK_ALARM; } else { return SKE_SUCCESS; } } /* function: wait till ske_hp expanding key is done * parameters: * return: none * caution: */ uint32_t ske_hp_expand_key_wait_till_done() { uint32_t state_val; while (!(g_ske_hp_reg->sr & 0x1)) { state_val = ske_hp_check_attack_state(); if (SKE_SUCCESS == state_val) { continue; } else { return state_val; } } return SKE_SUCCESS; } /* function: wait till ske_hp is waiting to input * parameters: * return: none * caution: */ uint32_t ske_hp_wait_till_input() { uint32_t state_val; while (!(g_ske_hp_reg->sr & (1 << 16))) { state_val = ske_hp_check_attack_state(); if (SKE_SUCCESS == state_val) { continue; } else { return state_val; } } return SKE_SUCCESS; } /* function: wait till ske_hp output is ready * parameters: * return: SKE_SUCCESS(success), other(error) * caution: */ uint32_t ske_hp_wait_till_output() { uint32_t state_val; while (!(g_ske_hp_reg->sr & (1 << 17))) { state_val = ske_hp_check_attack_state(); if (SKE_SUCCESS == state_val) { continue; } else { return state_val; } } return SKE_SUCCESS; } /* function: wait till ske_hp calculating is done * parameters: * return: SKE_SUCCESS(success), other(error) * caution: */ uint32_t ske_hp_calc_wait_till_done(void) { uint32_t state_val; while (!(g_ske_hp_reg->risr & 0x7)) { state_val = ske_hp_check_attack_state(); if (SKE_SUCCESS == state_val) { continue; } else { return state_val; } } return SKE_SUCCESS; } /* function: set ske_hp key * parameters: * key ------------------------ input, key in word buffer * idx ------------------------ input, key index, only 1 and 2 are valid * key_words ------------------ input, word length of key * return: none * caution: * 1. if idx is 1, set key1 register, else if idx is 2, set key2 register, * please make sure idx is valid */ void ske_hp_set_key_uint32(uint32_t *key, uint32_t idx, uint32_t key_words) { volatile uint32_t *key_reg; int32_t i; if (1 == idx) { key_reg = g_ske_hp_reg->key1; } else { key_reg = g_ske_hp_reg->key2; } for (i = key_words; i > 0; i--) { key_reg[i - 1] = key[key_words - i]; } } /* function: set ske_hp iv * parameters: * iv ------------------------- input, iv in word buffer * block_words ---------------- input, word length of ske_hp block * return: none * caution: * 1. please make sure the three parameters are valid */ void ske_hp_set_iv_uint32(uint32_t *iv, uint32_t block_words) { int32_t i; for (i = block_words; i > 0; i--) { g_ske_hp_reg->iv[i - 1] = iv[block_words - i]; } } #if (defined(SUPPORT_SKE_MODE_GCM) || defined(SUPPORT_SKE_MODE_CCM)) /* function: set aad bits(just for ske_hp ccm/gcm mode) * parameters: * aad_bytes ------------------ input, byte length of aad * return: none * caution: * 1. this function is just for CCM/GCM mode */ void ske_hp_set_aad_len_uint32(uint32_t aad_bytes) { g_ske_hp_reg->ske_a_len_l = ((aad_bytes) << 3) & 0xFFFFFFFF; g_ske_hp_reg->ske_a_len_h = aad_bytes >> (32 - 3); } #endif #if (defined(SUPPORT_SKE_MODE_GCM) || defined(SUPPORT_SKE_MODE_CCM) || \ defined(SUPPORT_SKE_MODE_XTS)) /* function: set plaintext/ciphertext bits(just for ske_hp ccm/gcm/xts mode) * parameters: * c_bytes -------------------- input, byte length of plaintext/ciphertext * return: none * caution: * 1. this function is just for CCM/GCM/XTS mode */ void ske_hp_set_c_len_uint32(uint32_t c_bytes) { g_ske_hp_reg->ske_c_len_l = ((c_bytes) << 3) & 0xFFFFFFFF; g_ske_hp_reg->ske_c_len_h = c_bytes >> (32 - 3); } #endif /* function: input one block * parameters: * in ------------------------- input, plaintext or ciphertext in word * buffer block_words ---------------- input, word length of ske_hp block * return: none * caution: * 1. in is a word buffer of only one block. */ void ske_hp_simple_set_input_block(uint32_t *in, uint32_t block_words) { int32_t i; for (i = block_words; i > 0; i--) { g_ske_hp_reg->m_din[i - 1] = in[block_words - i]; } } /* function: output one block * parameters: * out ------------------------ output, one block output of ske_hp in word * buffer block_words ---------------- input, word length of ske_hp block * return: none * caution: */ void ske_hp_simple_get_output_block(uint32_t *out, uint32_t block_words) { int32_t i; /* trigger to pop */ g_ske_hp_reg->ctrl |= 0x02; for (i = block_words; i > 0; i--) { out[block_words - i] = g_ske_hp_reg->m_dout[i - 1]; } } /* function: ske_hp expand key * parameters: none * return: none * caution: 1. must be called after ske_hp_set_crypto() and ske_hp_set_alg(), * and the key is set already. */ uint32_t ske_hp_expand_key(void) { uint32_t ret; g_ske_hp_reg->cfg |= ((0x1) << SKE_HP_UP_CFG_OFFSET); ske_hp_start(); ret = ske_hp_expand_key_wait_till_done(); if (SKE_SUCCESS != ret) { return ret; } g_ske_hp_reg->cfg &= ~((0x1) << SKE_HP_UP_CFG_OFFSET); return SKE_SUCCESS; } /************************* DMA *************************/ void dma_set_time_out_threshold(uint16_t cycle_threshold) { g_ske_hp_reg->dma_to = cycle_threshold; } #ifdef SKE_HP_DMA_FUNCTION /* function: basic ske_hp DMA operation * parameters: * ctx ------------------------ input, ske_ctx_t context pointer * in ------------------------- input, plaintext or ciphertext * out ------------------------ output, ciphertext or plaintext * in_words ------------------- input, word length of in, must be multiples * of block word length out_words ------------------ input, word length of out, * must be multiples of block word length return: SKE_SUCCESS(success), * other(error) caution: * 1. in_words & out_words must be multiples of block words. * 2. it could be without output, namely, out can be NULL, out_words can be * 0(for CBC_MAC/CMAC) */ uint32_t ske_hp_dma_operate(ske_ctx_t *ctx, uint32_t *in, uint32_t *out, uint32_t in_words, uint32_t out_words) { uint32_t ret; if (NULL == in) { return SKE_BUFFER_NULL; } /* src & dst addr low 32bits */ g_ske_hp_reg->dma_sa_l = (uint32_t)in; g_ske_hp_reg->dma_da_l = (uint32_t)out; /* src & dst addr high 32bits */ if (4 == (sizeof(uint32_t *))) { /* in this case, if using (((uint64_t)in)>>32), you may get 0xFFFFFFFF, * not 0 you expected! */ g_ske_hp_reg->dma_sa_h = 0x0; g_ske_hp_reg->dma_da_h = 0x0; } else { /* g_ske_hp_reg->dma_sa_h = (((uint64_t)in)>>32); */ /* g_ske_hp_reg->dma_da_h = (((uint64_t)out)>>32); */ } /* data word length */ g_ske_hp_reg->dma_rlen = in_words * 32; g_ske_hp_reg->dma_wlen = out_words * 32; ske_hp_start(); ret = ske_hp_calc_wait_till_done(); if (SKE_SUCCESS != ret) { uint32_clear(out, out_words); } else { } return ret; } #if (defined(SUPPORT_SKE_MODE_CMAC) || defined(SUPPORT_SKE_MODE_CMAC)) /* function: basic ske_hp DMA operation without output(for CBC_MAC/CMAC) * parameters: * ctx ------------------------ input, ske_ctx_t context pointer * in ------------------------- input, plaintext or ciphertext * in_words ------------------- input, word length of in, must be multiples * of block word length return: SKE_SUCCESS(success), other(error) caution: * 1. in_words must be a multiple of block words. */ uint32_t ske_hp_dma_operate_without_output(ske_ctx_t *ctx, uint32_t *in, uint32_t in_words) { volatile uint32_t flag_0 = 0; /* src addr low 32bits */ g_ske_hp_reg->dma_sa_l = (uint32_t)in; /* src addr high 32bits */ if (4 == (sizeof(uint32_t *))) { /* in this case, if using (((uint64_t)in)>>32), you may get 0xFFFFFFFF, * not 0 you expected! */ g_ske_hp_reg->dma_sa_h = flag_0; } else { /* g_ske_hp_reg->dma_sa_h = (((uint64_t)in)>>32); */ } /* data word length */ g_ske_hp_reg->dma_rlen = in_words * 32; g_ske_hp_reg->dma_wlen = flag_0; ske_hp_start(); return ske_hp_calc_wait_till_done(); } #endif #ifdef SKE_HP_DMA_LL_FUNCTION void ske_hp_dma_ll_operate(uint32_t *in, uint32_t *out, uint32_t wordLen, dma_ll_node_t *llp) { /* ll addr */ g_ske_hp_reg->dma_llp_l = ((uint64_t)llp) & 0xFFFFFFFF; g_ske_hp_reg->dma_llp_h = ((uint64_t)llp) >> 32; /* src addr */ g_ske_hp_reg->dma_sa_l = ((uint64_t)in) & 0xFFFFFFFF; g_ske_hp_reg->dma_sa_h = ((uint64_t)in) >> 32; /* dst addr */ g_ske_hp_reg->dma_da_l = ((uint64_t)out) & 0xFFFFFFFF; g_ske_hp_reg->dma_da_h = ((uint64_t)out) >> 32; /* data word length */ g_ske_hp_reg->dma_rlen = wordLen * 32; ske_hp_start(); ske_hp_calc_wait_till_done(); } #endif #endif /* function: update ske_hp some blocks without output * parameters: * ctx ------------------------ input, ske_ctx_t context pointer * in ------------------------- input, some blocks * bytes ---------------------- input, byte length of in * return: SKE_SUCCESS(success), other(error) * caution: * 1. please make sure the bytes is a multiple of block byte length * ctx->block_bytes * 2. this function is called by CCM(input aad)/GCM(input aad)/CMAC/CBC-MAC * mode */ uint32_t ske_hp_update_blocks_no_output(ske_ctx_t *ctx, uint8_t *in, uint32_t bytes) { uint32_t in_word_align, is_ccm_gcm_mode = 0; uint32_t tmp_in[4]; uint32_t i; uint32_t ret; if (((uint32_t)in) & 3) { in_word_align = 0; } else { in_word_align = 1; } i = (g_ske_hp_reg->cfg & 0xF0000000) >> 28; switch (i) { #if (defined(SUPPORT_SKE_MODE_GCM)) case SKE_MODE_GCM: #endif #if (defined(SUPPORT_SKE_MODE_CCM)) case SKE_MODE_CCM: #endif #if (defined(SUPPORT_SKE_MODE_GCM) || defined(SUPPORT_SKE_MODE_CCM)) is_ccm_gcm_mode = 1; break; #endif default: is_ccm_gcm_mode = 0; } /* input one block ---> calculating ---> output one block */ for (i = 0; i < bytes; i += ctx->block_bytes) { if (in_word_align) { ske_hp_simple_set_input_block((uint32_t *)in, ctx->block_words); } else { memcpy_(tmp_in, in, ctx->block_bytes); ske_hp_simple_set_input_block((uint32_t *)tmp_in, ctx->block_words); } ske_hp_start(); if (is_ccm_gcm_mode) { ret = ske_hp_wait_till_input(); if (SKE_SUCCESS != ret) { return ret; } else { ; } } else { ret = ske_hp_calc_wait_till_done(); if (SKE_SUCCESS != ret) { return ret; } else { /* trigger to pop, this can not be deleted */ g_ske_hp_reg->ctrl |= 0x2; } } in += ctx->block_bytes; } return SKE_SUCCESS; } /* function: update ske_hp some blocks and get the same number of blocks * parameters: * ctx ------------------------ input, ske_ctx_t context pointer * in ------------------------- input, some blocks * out ------------------------ output, the same number of blocks * bytes ---------------------- input, byte length of in * return: SKE_SUCCESS(success), other(error) * caution: * 1. please make sure the bytes is a multiple of block byte length * ctx->block_bytes */ uint32_t ske_hp_update_blocks_internal(ske_ctx_t *ctx, uint8_t *in, uint8_t *out, uint32_t bytes) { uint8_t *out_bak = out; uint32_t in_word_align, out_word_align; uint32_t tmp_in[4]; uint32_t i; uint32_t ret; if (((uint32_t)in) & 3) { in_word_align = 0; } else { in_word_align = 1; } if (((uint32_t)out) & 3) { out_word_align = 0; } else { out_word_align = 1; } /* input one block ---> calculating ---> output one block */ for (i = 0; i < bytes; i += ctx->block_bytes) { if (in_word_align) { ske_hp_simple_set_input_block((uint32_t *)in, ctx->block_words); } else { memcpy_(tmp_in, in, ctx->block_bytes); ske_hp_simple_set_input_block((uint32_t *)tmp_in, ctx->block_words); } ske_hp_start(); ret = ske_hp_wait_till_output(); if (SKE_SUCCESS != ret) { if (out_word_align) { uint32_clear((uint32_t *)out_bak, bytes / 4); } else { memset_(out_bak, 0, bytes); } return ret; } else { ; } if (out_word_align) { ske_hp_simple_get_output_block((uint32_t *)out, ctx->block_words); } else { ske_hp_simple_get_output_block((uint32_t *)tmp_in, ctx->block_words); memcpy_(out, tmp_in, ctx->block_bytes); } in += ctx->block_bytes; out += ctx->block_bytes; } return SKE_SUCCESS; }