/*
 * Copyright (C) 2015-2017 Alibaba Group Holding Limited
 */

#include "id2_config.h"
#include "id2_plat.h"
#include "id2_priv.h"
#include "id2_client.h"
#include "ali_crypto.h"
#include "km.h"

#define ID2_CLIENT_VERSION_NUMBER   0x00030100

/* id2 auth code type */
#define TYPE_CLIENT_CHALLENGE               0
#define TYPE_CLIENT_TIMESTAMP               1
#define TYPE_CLIENT_CHALLENGE_WITH_EXTRA    2
#define TYPE_CLIENT_TIMESTAMP_WITH_EXTRA    3

#define TYPE_SERVER_CHALLENGE               10
#define TYPE_SERVER_CHALLENGE_WITH_EXTRA    11

#define MAX_HASH_LEN                        64
#define MAX_SYMM_BLOCK_LEN                  16

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_ECC  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM1  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM2  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)
#define MAX_SIGN_LEN                        128
#define ID2_AUTH_CODE_BUF_LEN               256
#else
#define MAX_SIGN_LEN                        256
#define ID2_AUTH_CODE_BUF_LEN               512
#endif

#define ID2_LOCAL_DEVICE_RANDOM_LEN         16

static uint8_t s_id2_id[ID2_ID_MAX_LEN + 1] = {0};
static uint8_t s_id2_id_len = 0;
static uint8_t s_id2_init_count = 0;

static void _dump_id2_conf_info(void)
{
    ls_osa_print("ID2 Client Version: 0x%08x\n", ID2_CLIENT_VERSION_NUMBER);
    ls_osa_print("ID2 Client Build Time: %s %s\n", __DATE__, __TIME__);

    ls_osa_print("-------------------------------------------\n");

#if defined(CONFIG_ID2_DEBUG)
    ls_osa_print("CONFIG_ID2_DEBUG is defined!\n");
#else
    ls_osa_print("CONFIG_ID2_DEBUG is not defined!\n");
#endif

#if defined(CONFIG_ID2_OTP)
    ls_osa_print("CONFIG_ID2_OTP is defined!\n");
#else
    ls_osa_print("CONFIG_ID2_OTP is not defined!\n");
#endif

    if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_3DES");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES){
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_AES");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_RSA) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_RSA");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_ECC) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_ECC");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM1) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_SM1");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM2) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_SM2");
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4) {
        ls_osa_print("CONFIG_ID2_KEY_TYPE: %s\n", "ID2_KEY_TYPE_SM4");
    }

    ls_osa_print("-------------------------------------------\n");
}

static void _id2_inverse(uint8_t* id2, uint32_t id_len)
{
    uint8_t i = 0;
    uint8_t hex = 0;

    for (i = 0; i < id_len; i++) {
        if (0 != id2_is_hex_char(id2[i])) {
            hex = id2_char_to_hex(id2[i]);
            hex = 0x0F - hex;
            id2[i] = id2_hex_to_char(hex);
        }
    }
}

static irot_result_t _id2_hash(uint8_t* data, uint32_t data_len,
                               uint8_t* hash, uint32_t* hash_len)
{
    int ret;
    digest_t type;

#if CONFIG_ID2_HASH_TYPE == ID2_HASH_TYPE_SHA256
    type = DIGEST_TYPE_SHA256;
#elif CONFIG_ID2_HASH_TYPE == ID2_HASH_TYPE_SM3
    type = DIGEST_TYPE_SM3;
#endif

    ret = id2_plat_hash_sum(data, data_len, hash, hash_len, type);
    if (ret < 0) {
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM1  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)
static irot_result_t _id2_sym_cipher(uint8_t* in, uint32_t in_len,
                              uint8_t* out, uint32_t* out_len, int is_enc)
{
    uint32_t km_ret;
    uint32_t block_len;
    uint32_t i, padding;
    size_t output_len;
    km_sym_param km_param;

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES)
    block_len = ID2_DES_BLOCK_SIZE;
    km_param.key_type = KM_DES3;
#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES)
    block_len = ID2_AES_BLOCK_SIZE;
    km_param.key_type = KM_AES;
#else
    block_len = ID2_SM4_BLOCK_SIZE;
    km_param.key_type = KM_SM4;
#endif

    id2_log_hex_dump("id2 cipher input:", in, in_len);

    if (in_len % block_len != 0) {
        id2_log_error("invalid input length, %d.\n", in_len);
        return IROT_ERROR_BAD_PARAMETERS;
    }

    km_param.cipher_param.block_mode = KM_ECB;
    km_param.cipher_param.padding_type = KM_NO_PADDING;
    km_param.cipher_param.purpose_type = is_enc ? KM_PURPOSE_ENCRYPT : KM_PURPOSE_DECRYPT;

    output_len = *out_len;
    km_ret = km_cipher(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                       &km_param, NULL, 0, (uint8_t*)in, in_len, out, &output_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km cipher error, 0x%x.\n",km_ret);
        return IROT_ERROR_GENERIC;
    }

    if (output_len < block_len || output_len % block_len != 0) {
        id2_log_error("invalid output length, %d.\n", output_len);
        return IROT_ERROR_GENERIC;
    }

    id2_log_hex_dump("id2 cipher output:", out, output_len);

    if (is_enc) {
        *out_len = output_len;
    } else {
        id2_log_debug("id2 cipher pkcs5 unpadding.\n");

        padding = out[output_len - 1];
        if (padding > block_len) {
            id2_log_error("invalid pkcs5 padding, %d\n", padding);
            return IROT_ERROR_GENERIC;
        }

        for (i = padding; i > 0; i--) {
            if (out[output_len - i] != padding) {
                id2_log_error("id2 pkcs5 unpadding fail.\n");
                return IROT_ERROR_GENERIC;
            }
        }

        *out_len = output_len - padding;
    }

    return IROT_SUCCESS;
}

static irot_result_t _id2_sign(uint8_t* sign_in, uint32_t sign_in_len,
                               uint8_t* sign_out, uint32_t* sign_out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint8_t hash_buf[MAX_HASH_LEN + MAX_SYMM_BLOCK_LEN];
    uint32_t hash_len = sizeof(hash_buf);
    uint32_t input_len;
    uint32_t block_len;
    uint32_t i, padding;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    ret = _id2_hash(sign_in, sign_in_len, hash_buf, &hash_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 hash fail, %d.\n", ret);
        return ret;
    }

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES)
    block_len = ID2_DES_BLOCK_SIZE;
#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES)
    block_len = ID2_AES_BLOCK_SIZE;
#else
    block_len = ID2_SM4_BLOCK_SIZE;
#endif

    padding = block_len - hash_len % block_len;
    input_len = hash_len + padding;

    for (i = 0; i < padding; i++) {
        hash_buf[hash_len + i] = padding;
    }

    ret = _id2_sym_cipher(hash_buf, input_len, sign_out, sign_out_len, 1);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sym cipher fail, %d.\n", ret);
        return ret;
    }

    id2_log_hex_dump("id2 sign output:", sign_out, *sign_out_len);

    return IROT_SUCCESS;
}

static irot_result_t _id2_verify(uint8_t* sign_in, uint32_t sign_in_len,
                                 uint8_t* sign_out, uint32_t sign_out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint8_t out_data[MAX_SIGN_LEN];
    uint32_t out_len = MAX_SIGN_LEN;

    ret = _id2_sign(sign_in, sign_in_len, out_data, &out_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sign error.\n");
        return ret;
    }

    if (out_len != sign_out_len) {
        id2_log_error("invalid sign length, %d %d.\n", out_len, sign_out_len);
        return IROT_ERROR_GENERIC;
    }

    if (memcmp(out_data, sign_out, sign_out_len) != 0) {
        id2_log_error("signature compare error.\n");
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_RSA)


irot_result_t _id2_asym_decrypt(uint8_t* in, uint32_t in_len,
                                uint8_t* out, uint32_t* out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    km_enc_param enc_params;
    uint32_t km_ret;
    uint32_t out_buf_len = *out_len;
    uint32_t out_data_len = 0;

    if (in_len % ID2_RSA_BLOCK_SIZE != 0) {
        id2_log_error("invalid input length, %d\n", in_len);
        return IROT_ERROR_BAD_PARAMETERS;
    }

    enc_params.padding_type = KM_PKCS1;
    while (in_len > 0) {
        *out_len = out_buf_len;

        /* decrypt one block each time */
        km_ret = km_asym_decrypt(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                                 &enc_params, in, in_len, out, out_len);
        if (km_ret != KM_SUCCESS) {
            id2_log_error("km asym decrypt error, 0x%08x.\n", km_ret);
            ret = IROT_ERROR_GENERIC;
            break;
        }

        in += ID2_RSA_BLOCK_SIZE;
        out += *out_len;
        out_data_len += *out_len;

        in_len -= ID2_RSA_BLOCK_SIZE;
        out_buf_len -= *out_len;
    }

    *out_len = out_data_len;

    return ret;
}

static irot_result_t _id2_sign(uint8_t* sign_in, uint32_t sign_in_len,
                               uint8_t* sign_out, uint32_t* sign_out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint8_t hash_buf[MAX_HASH_LEN];
    uint32_t hash_len = MAX_HASH_LEN;
    km_sign_param sign_params;
    uint32_t km_ret;

    sign_params.digest_type = KM_SHA256;
    sign_params.padding_type = KM_PKCS1;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    ret = _id2_hash(sign_in, sign_in_len, hash_buf, &hash_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 hash fail, %d.\n", ret);
        return ret;
    }

    km_ret = km_sign(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                     &sign_params, hash_buf, hash_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km sign fail, 0x%08x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    id2_log_hex_dump("id2 sign output:", sign_out, *sign_out_len);

    return IROT_SUCCESS;
}

static irot_result_t _id2_verify(uint8_t* sign_in, uint32_t sign_in_len,
                                 uint8_t* sign_out, uint32_t sign_out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    km_sign_param sign_params;
    uint8_t hash_buf[MAX_HASH_LEN];
    uint32_t hash_len = MAX_HASH_LEN;
    uint32_t km_ret;

    sign_params.digest_type = KM_SHA256;
    sign_params.padding_type = KM_PKCS1;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    ret = _id2_hash(sign_in, sign_in_len, hash_buf, &hash_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 hash fail, %d.\n", ret);
        return ret;
    }

    km_ret = km_verify(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                       &sign_params, hash_buf, hash_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km verify fail, 0x%08x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_ECC)

irot_result_t _id2_asym_decrypt(uint8_t* in, uint32_t in_len,
                                uint8_t* out, uint32_t* out_len)
{
    km_enc_param enc_params;
    uint32_t km_ret;

    enc_params.padding_type = KM_NO_PADDING;

    km_ret = km_asym_decrypt(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                             &enc_params, in, in_len, out, out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km_asym_decrypt fail, 0x%x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

static irot_result_t _id2_sign(uint8_t* sign_in, uint32_t sign_in_len,
                               uint8_t* sign_out, uint32_t* sign_out_len)
{
    km_sign_param sign_params;
    uint32_t km_ret;

    sign_params.padding_type = KM_NO_PADDING;
    sign_params.digest_type = KM_SHA256;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    km_ret = km_msg_sign(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                &sign_params, NULL, 0, sign_in, sign_in_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km msg sign fail, 0x%x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    id2_log_hex_dump("id2 sign output:", sign_out, *sign_out_len);

    return IROT_SUCCESS;
}

static irot_result_t _id2_verify(uint8_t* sign_in, uint32_t sign_in_len,
                                 uint8_t* sign_out, uint32_t sign_out_len)
{
    km_sign_param sign_params;
    uint32_t km_ret;

    sign_params.padding_type = KM_NO_PADDING;
    sign_params.digest_type = KM_SHA256;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    km_ret = km_msg_verify(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                &sign_params, NULL, 0, sign_in, sign_in_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km msg verify fail, 0x%x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM2)

irot_result_t _id2_asym_decrypt(uint8_t* in, uint32_t in_len,
                                uint8_t* out, uint32_t* out_len)
{
    irot_result_t ret = IROT_SUCCESS;
    km_enc_param enc_params;
    uint32_t km_ret;

    enc_params.padding_type = KM_NO_PADDING;

    km_ret = km_asym_decrypt(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                             &enc_params, in, in_len, out, out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km asym decrypt error, 0x%08x.\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    return ret;
}

static irot_result_t _id2_sign(uint8_t* sign_in, uint32_t sign_in_len,
                               uint8_t* sign_out, uint32_t* sign_out_len)
{
    km_sign_param sign_params;
    char *id = "1234567812345678";
    uint32_t km_ret;

    sign_params.digest_type = KM_SM3;
    sign_params.padding_type = KM_NO_PADDING;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    km_ret = km_msg_sign(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                    &sign_params, (uint8_t *)id, strlen(id),
                    sign_in, sign_in_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km msg sign fail, 0x%08x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    id2_log_hex_dump("id2 sign output:", sign_out, *sign_out_len);

    return IROT_SUCCESS;
}

static irot_result_t _id2_verify(uint8_t* sign_in, uint32_t sign_in_len,
                                 uint8_t* sign_out, uint32_t sign_out_len)
{
    km_sign_param sign_params;
    char *id = "1234567812345678";
    uint32_t km_ret;

    sign_params.digest_type = KM_SM3;
    sign_params.padding_type = KM_NO_PADDING;

    id2_log_hex_dump("id2 sign input:", sign_in, sign_in_len);

    km_ret = km_msg_verify(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                    &sign_params, (uint8_t *)id, strlen(id),
                    sign_in, sign_in_len, sign_out, sign_out_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km msg verify fail, 0x%x\n", km_ret);
        return IROT_ERROR_GENERIC;
    }

    return IROT_SUCCESS;
}

#endif

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES)

static irot_result_t _id2_dkey_do_cipher(
                          uint8_t* key, uint32_t key_len,
                          uint8_t* in, uint32_t in_len, uint8_t* out)
{
    int ret = 0;
    size_t ctx_size;
    void* aes_ctx = NULL;
    ali_crypto_result result;

    if (key == NULL || key_len == 0 ||
        in == NULL || in_len == 0 || out == NULL) {
        id2_log_error("invalid input args\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    result = ali_aes_get_ctx_size(AES_ECB, &ctx_size);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("aes get ctx size fail, 0x%x\n", result);
        return IROT_ERROR_GENERIC;
    }

    aes_ctx = ls_osa_malloc(ctx_size);
    if (aes_ctx == NULL) {
        id2_log_error("out of mem, %d\n", (int)ctx_size);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_aes_init(AES_ECB, true, key, NULL, key_len, NULL, aes_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("aes init fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_aes_process(in, out, in_len, aes_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("aes process fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_aes_finish(NULL, 0, NULL, NULL, SYM_NOPAD, aes_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("aes finish fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    ret = IROT_SUCCESS;

_out:
    ls_osa_free(aes_ctx);

    return ret;
}

#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)

static irot_result_t _id2_dkey_do_cipher(
                          uint8_t* key, uint32_t key_len,
                          uint8_t* in, uint32_t in_len, uint8_t* out)
{

    int ret = 0;
    size_t ctx_size;
    void* sm4_ctx = NULL;
    ali_crypto_result result;

    if (key == NULL || key_len == 0 ||
        in == NULL || in_len == 0 || out == NULL) {
        id2_log_error("invalid input args\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    result = ali_sm4_get_ctx_size(SM4_ECB, &ctx_size);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("sm4 get ctx size fail, 0x%x\n", result);
        return IROT_ERROR_GENERIC;
    }

    sm4_ctx = ls_osa_malloc(ctx_size);
    if (sm4_ctx == NULL) {
        id2_log_error("out of mem, %d\n", (int)ctx_size);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_sm4_init(SM4_ECB, true, key, NULL, key_len, NULL, sm4_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("sm4 init fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_sm4_process(in, out, in_len, sm4_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("sm4 process fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    result = ali_sm4_finish(NULL, 0, NULL, NULL, SYM_NOPAD, sm4_ctx);
    if (result != ALI_CRYPTO_SUCCESS) {
        id2_log_error("sm4 finish fail, 0x%x\n", result);
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    ret = IROT_SUCCESS;

_out:
    ls_osa_free(sm4_ctx);

    return ret;
}

#else
static irot_result_t _id2_dkey_do_cipher(
                          uint8_t* key, uint32_t key_len,
                          uint8_t* in, uint32_t in_len, uint8_t* out)
{
     id2_log_error("not supported!\n");

     return IROT_ERROR_NOT_SUPPORTED;
}
#endif


#ifdef CONFIG_ID2_AUTH_CODE_HARDEN
static irot_result_t id2_auth_code_encode(uint8_t *auth_code, uint32_t in_len, uint32_t *out_len)
{
    irot_result_t result;
    uint32_t block_len;
    uint32_t auth_data_len;
    uint8_t *auth_data = NULL;
    int ret;

    if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES) {
        block_len = ID2_AES_BLOCK_SIZE;
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES) {
        block_len = ID2_DES_BLOCK_SIZE;
    } else if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4) {
        block_len = ID2_SM4_BLOCK_SIZE;
    } else {
        id2_log_error("not support this type, %d\n", CONFIG_ID2_KEY_TYPE);
        return IROT_ERROR_NOT_SUPPORTED;
    }

    auth_data_len = in_len + (block_len - in_len % block_len);
    auth_data_len = 2 + ((auth_data_len + 2) / 3) * 4;

    if (*out_len <= auth_data_len) {
        id2_log_error("short buffer %d %d\n", *out_len, auth_data_len);
        return IROT_ERROR_SHORT_BUFFER;
    }

    ret = pkcs5_pading(auth_code, in_len, &in_len, block_len);
    if (ret != 0) {
        id2_log_error("pkcs5 padding data error\n");
        return IROT_ERROR_GENERIC;
    }

    auth_data = (uint8_t *)ls_osa_malloc(in_len);
    if (auth_data == NULL) {
        id2_log_error("out of mem, %d\n", in_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    }

    result = _id2_sym_cipher(auth_code, in_len, auth_data, &auth_data_len, 1);
    if (result != IROT_SUCCESS) {
        id2_log_error("id2 sym cipher error.\n");
        return result;
    }

    memset(auth_code, 0, *out_len);
    auth_code[0] = '9';
    auth_code[1] = '9';
    auth_code[2] = '~';

    ret = id2_plat_base64_encode(auth_data, auth_data_len, auth_code + 3, out_len);
    if (ret < 0) {
        id2_log_error("sym cipher encrypt error.\n");
        result = IROT_ERROR_GENERIC;
        goto _out;
    }

    *out_len = *out_len + 3;

_out:
    if (auth_data != NULL) {
        ls_osa_free(auth_data);
    }

    return result;
}

static irot_result_t id2_auth_code_decode(uint8_t *auth_code, uint32_t *auth_code_len)
{
    irot_result_t result;
    uint32_t auth_data_len;
    uint8_t *auth_data = NULL;
    int ret;

    if (auth_code[0] != '9' || auth_code[1] != '9') {
        id2_log_error("invalid auth code type(%d%d)\n", auth_code[0], auth_code[1]);
        return IROT_ERROR_GENERIC;
    }

    auth_data = (uint8_t *)ls_osa_malloc(*auth_code_len);
    if (auth_data == NULL) {
        id2_log_error("out of mem, %d\n", *auth_code_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    }

    ret = id2_plat_base64_decode(auth_code + 3, *auth_code_len - 3, auth_data, &auth_data_len);
    if (ret < 0) {
        id2_log_error("base64 decode error.\n");
        result = IROT_ERROR_GENERIC;
        goto _out;
    }

    memset(auth_code, 0, *auth_code_len);

    result = _id2_sym_cipher(auth_data, auth_data_len, auth_code, auth_code_len, 0);
    if (result != IROT_SUCCESS) {
        id2_log_error("id2 sym cipher error.\n");
        goto _out;
    }

_out:
    if (auth_data != NULL) {
        ls_osa_free(auth_data);
    }

    return result;
}
#endif

static irot_result_t _id2_gen_auth_code(uint8_t auth_code_type, const char* server_random,
                                        const uint8_t* extra, uint32_t extra_len,
                                        uint8_t* auth_code, uint32_t* auth_code_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint32_t id2_len = ID2_ID_MAX_LEN;
    uint32_t id2_auth_len = 0;
    uint32_t device_random[2];
    uint8_t id2_str[ID2_ID_MAX_LEN + 1] = {0};
    char device_random_str[ID2_LOCAL_DEVICE_RANDOM_LEN + 1] = {0};

    uint8_t* sign_in = NULL;
    uint8_t* sign_out = NULL;
    uint8_t* sign_base64 = NULL;
    uint32_t sign_in_len = 0;
    uint32_t sign_out_len = 0;
    uint32_t sign_base64_len = 0;

    if (auth_code == NULL || auth_code_len == NULL ||
        server_random == NULL) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (strlen(server_random) == 0 ||
        strlen(server_random) > ID2_MAX_SERVER_RANDOM_LEN) {
        id2_log_error("invalid server random length, %d.\n", strlen(server_random));
        return IROT_ERROR_BAD_PARAMETERS;
    }

    /* check extra */
    if (extra_len > ID2_MAX_EXTRA_LEN || (
        extra != NULL && extra_len == 0)) {
        id2_log_error("invalid extra data length, %d.\n", extra_len);
        return IROT_ERROR_EXCESS_DATA;
    }

    /* check authcode */
    if (*auth_code_len < ID2_AUTH_CODE_BUF_LEN) {
        id2_log_error("auth code short buffer, %d %d.\n",
                       *auth_code_len, ID2_AUTH_CODE_BUF_LEN);
        *auth_code_len = ID2_AUTH_CODE_BUF_LEN;
        return IROT_ERROR_SHORT_BUFFER;
    }

    /* get ID */
    ret = id2_client_get_id(id2_str, &id2_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("get ID error.\n");
        return ret;
    }

    /* device random */
    id2_plat_get_random((uint8_t*)&device_random, sizeof(device_random));
    if (ls_osa_snprintf(device_random_str, sizeof(device_random_str),
                        "%08X%08X", device_random[0], device_random[1]) < 0) {
        id2_log_error("device random format exchange error.\n");
        return IROT_ERROR_GENERIC;
    }

    /* signature input = ID2 + device random + server random + extra */
    sign_in_len = id2_len + strlen(device_random_str) + strlen(server_random) + extra_len;
    sign_in = (uint8_t*)ls_osa_malloc(sign_in_len + 1);
    if (sign_in == NULL) {
        id2_log_error("out of mem, %d.\n", sign_in_len + 1);
        return IROT_ERROR_OUT_OF_MEMORY;
    } else {
        memset(sign_in, 0x00, sign_in_len + 1);
    }

    if (extra == NULL) {
        extra = (const uint8_t *)"";
    }

    if (ls_osa_snprintf((char*)sign_in, sign_in_len + 1, "%s%s%s%s",
               id2_str, device_random_str, server_random, extra) < 0) {
        id2_log_error("sign data format exchange error.\n");
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    sign_out = (uint8_t*)ls_osa_malloc(MAX_SIGN_LEN);
    if (sign_out == NULL) {
        id2_log_error("out of mem, %d.\n", MAX_SIGN_LEN);
        ret = IROT_ERROR_OUT_OF_MEMORY;
        goto _out;
    } else {
        memset(sign_out, 0x00, MAX_SIGN_LEN);
    }

    /* ID2 signature */
    sign_out_len = MAX_SIGN_LEN;
    ret = _id2_sign(sign_in, sign_in_len, sign_out, &sign_out_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sign error.\n");
        goto _out;
    }

    /* base64 encode */
    sign_base64_len = sign_out_len * 2;
    sign_base64 = (uint8_t*)ls_osa_malloc(sign_base64_len);
    if (sign_base64 == NULL) {
        id2_log_error("out of mem, %d.\n", sign_base64_len);
        ret = IROT_ERROR_OUT_OF_MEMORY;
        goto _out;
    }

    memset(sign_base64, 0x00, sign_base64_len);
    ret = id2_plat_base64_encode(sign_out, sign_out_len, sign_base64, &sign_base64_len);
    if (ret < 0) {
        id2_log_error("base64 encode error.\n");
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    id2_log_debug("base64 output: %s\n", sign_base64);

    /* auth_code = auth type ~ hash type ~ device random ~ server random ~ sign base64 */
    id2_auth_len = (uint32_t)(1 + 1 + strlen(device_random_str) + strlen(server_random) + sign_base64_len + 4);
    if (*auth_code_len <= id2_auth_len) {
        id2_log_error("short buffer: %d %d.\n", *auth_code_len, id2_auth_len);
        ret = IROT_ERROR_SHORT_BUFFER;
        goto _out;
    }

    if (ls_osa_snprintf((char*)auth_code, id2_auth_len + 1, "%d~%d~%s~%s~%s",
               auth_code_type, CONFIG_ID2_HASH_TYPE, device_random_str, server_random, sign_base64) < 0) {
        id2_log_error("auth code format exchange error.\n");
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

#ifdef CONFIG_ID2_AUTH_CODE_HARDEN
    if (id2_auth_code_encode((uint8_t *)auth_code, id2_auth_len, auth_code_len) < 0) {
        id2_log_error("auth code encode error.\n");
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }
#else
    *auth_code_len = id2_auth_len;
#endif

    id2_log_debug("client authcode: [%d] %s\n", strlen((char *)auth_code), auth_code);

_out:
    if (sign_in) {
        ls_osa_free(sign_in);
    }
    if (sign_out) {
        ls_osa_free(sign_out);
    }
    if (sign_base64) {
        ls_osa_free(sign_base64);
    }

    return ret;
}

int is_id2_client_inited(void)
{
    return s_id2_init_count;
}

irot_result_t id2_client_init(void)
{
    uint32_t km_ret;
    uint32_t irot_type;

    id2_log_debug("[id2_client_init enter.]\n");

    _dump_id2_conf_info();

    if (s_id2_init_count++ == 0) {
        km_ret = km_init();
        if (km_ret != KM_SUCCESS) {
            id2_log_error("km init fail, %08x.\n", km_ret);
            return IROT_ERROR_GENERIC;
        }

        s_id2_id_len = 0;
        memset(s_id2_id, 0x00, ID2_ID_MAX_LEN + 1);
    } else {
        id2_log_info("id2 init count, %d\n", s_id2_init_count);
    }

    irot_type = km_get_irot_type();
    id2_log_info("id2 irot type, %d\n", irot_type);

    return IROT_SUCCESS;
}

irot_result_t id2_client_cleanup(void)
{
    id2_log_debug("[id2_client_cleanup enter.]\n");

    if (s_id2_init_count == 0) {
        id2_log_info("id2 has not been inited\n");
        return IROT_SUCCESS;
    }

    if (--s_id2_init_count == 0) {
        km_cleanup();
    } else {
        id2_log_info("id2 init count, %d\n", s_id2_init_count);
    }

    return IROT_SUCCESS;
}

irot_result_t id2_client_get_version(uint32_t* version)
{
    id2_log_debug("[id2_client_get_version enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (version == NULL) {
        id2_log_error("invalid input arg.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    *version = ID2_CLIENT_VERSION_NUMBER;

    return IROT_SUCCESS;
}

irot_result_t id2_client_get_id(uint8_t* id, uint32_t* len)
{
    uint32_t km_ret;
    uint32_t id_len = ID2_ID_MAX_LEN;

    id2_log_debug("[id2_client_get_id enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (id == NULL || len == NULL) {
        id2_log_error("id or len is NULL.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (*len < ID2_ID_MAX_LEN) {
        id2_log_error("short buffer, %d.\n", *len);
        *len = ID2_ID_MAX_LEN;
        return IROT_ERROR_SHORT_BUFFER;
    }

    if (s_id2_id_len == 0) {
        km_ret = km_get_id2(id, len);
        if (km_ret == KM_ERR_ITEM_NOT_FOUND) {
            id2_log_debug("item not found\n");
            return IROT_ERROR_ITEM_NOT_FOUND;
        } else if (km_ret != KM_SUCCESS) {
            id2_log_error("km get id fail, 0x%08x.\n", km_ret);
            return IROT_ERROR_GENERIC;
        }

        if (*len > ID2_ID_MAX_LEN) {
            id2_log_error("id2 excess limit, %d %d\n", *len, ID2_ID_MAX_LEN);
            return IROT_ERROR_GENERIC;
        }

        /* For special legacy case (not suggest),
         * ID has been converted into hex format in chip
         */
        if (*len < ID2_ID_MIN_LEN) {
            id2_hex_to_string(id, *len, s_id2_id, &id_len);

            *len = id_len;
            memcpy(id, s_id2_id, *len);
        } else {
            memcpy(s_id2_id, id, *len);
        }

        s_id2_id_len = *len;

        id2_log_info("ID2: %s\n", s_id2_id);
    } else {
        memcpy(id, s_id2_id, s_id2_id_len);
        *len = s_id2_id_len;
    }

    return IROT_SUCCESS;
}

irot_result_t id2_client_get_challenge_auth_code(const char* server_random,
                                                 const uint8_t* extra, uint32_t extra_len,
                                                 uint8_t* auth_code, uint32_t* auth_code_len)
{
    uint8_t auth_code_type;

    id2_log_debug("[id2_client_get_challenge_auth_code enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    auth_code_type = (extra == NULL) ? TYPE_CLIENT_CHALLENGE: TYPE_CLIENT_CHALLENGE_WITH_EXTRA;
    return _id2_gen_auth_code(auth_code_type, server_random,
                              extra, extra_len, auth_code, auth_code_len);
}

irot_result_t id2_client_get_timestamp_auth_code(const char* timestamp,
                                                 const uint8_t* extra, uint32_t extra_len,
                                                 uint8_t* auth_code, uint32_t* auth_code_len)
{
    uint8_t auth_code_type;

    id2_log_debug("[id2_client_get_timestamp_auth_code enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    auth_code_type = (extra == NULL) ? TYPE_CLIENT_TIMESTAMP : TYPE_CLIENT_TIMESTAMP_WITH_EXTRA;
    return _id2_gen_auth_code(auth_code_type, timestamp,
                              extra, extra_len, auth_code, auth_code_len);
}

irot_result_t id2_client_encrypt(const char *seed,
                  const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len)
{
    irot_result_t ret;
    uint8_t key[ID2_DERIV_KEY_LEN];
    uint32_t seed_len;
    uint32_t block_len;
    uint32_t key_len;
    uint32_t i, padding;
    uint8_t *in_buf = NULL;

    id2_log_debug("[id2_client_decrypt enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (in == NULL || in_len == 0 || out == NULL || out_len == NULL) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (seed == NULL) {
        seed = "";
    }

    seed_len = strlen(seed);
    if (seed_len > ID2_MAX_SEED_LEN) {
        id2_log_error("seed is excess data.\n");
        return IROT_ERROR_EXCESS_DATA;
    }

    if (in_len > ID2_MAX_CRYPT_LEN) {
        id2_log_error("invalid input data length, %d.\n", in_len);
        return IROT_ERROR_EXCESS_DATA;
    }

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES)
    block_len = ID2_AES_BLOCK_SIZE;
    key_len = 32;
#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)
    block_len = ID2_SM4_BLOCK_SIZE;
    key_len = 16;
#else
    id2_log_error("not support this key type, %d\n", CONFIG_ID2_KEY_TYPE);
    return IROT_ERROR_NOT_SUPPORTED;
#endif

    ret = id2_client_derive_key(seed, key, key_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 derive key fail, %d\n", ret);
        return ret;
    }

    padding = block_len - in_len % block_len;
    in_buf = ls_osa_malloc(in_len + padding);
    if (in_buf == NULL) {
        id2_log_error("out of mem, %d\n", in_len + padding);
        ret = IROT_ERROR_OUT_OF_MEMORY;
        goto _out;
    }

    memcpy(in_buf, in, in_len);
    for (i = 0; i < padding; i++) {
        in_buf[in_len + i] = padding;
    }

    in_len += padding;
    if (*out_len < in_len) {
        id2_log_error("short buffer, %d %d\n", out_len, in_len);
        ret = IROT_ERROR_SHORT_BUFFER;
        goto _out;
    } else {
        *out_len = in_len;
    }

    ret = _id2_dkey_do_cipher(key, key_len, in_buf, in_len, out);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 derived key cipher fail, %d\n", ret);
        goto _out;
    }

_out:
    if (in_buf != NULL) {
        ls_osa_free(in_buf);
    }

    return ret;
}

irot_result_t id2_client_decrypt(const uint8_t* in, uint32_t in_len, uint8_t* out, uint32_t* out_len)
{
    irot_result_t ret;
    uint8_t* data = NULL;
    uint32_t data_len;

    id2_log_debug("[id2_client_decrypt enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (in == NULL || in_len == 0 || out == NULL || out_len == NULL) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (in_len > ID2_MAX_CRYPT_LEN) {
        id2_log_error("invalid input data length, %d.\n", in_len);
        return IROT_ERROR_EXCESS_DATA;
    }

    data_len = in_len;
    data = (uint8_t*)ls_osa_malloc(data_len);
    if (data == NULL) {
        id2_log_error("out of mem, %d.\n", data_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    }

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM1  || \
     CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)
    ret = _id2_sym_cipher((uint8_t *)in, in_len, data, &data_len, 0);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sym decrypt fail, %d\n", ret);
        goto _out;
    }
#else
    ret = _id2_asym_decrypt((uint8_t *)in, in_len, data, &data_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 asym decrypt fail, %d\n", ret);
        goto _out;
    }
#endif

    if (*out_len < data_len) {
        id2_log_error("short buffer, %d %d.\n", *out_len, data_len);
        ret = IROT_ERROR_SHORT_BUFFER;
        goto _out;
    }

    memcpy(out, data, data_len);
    *out_len = data_len;

_out:
    if (data != NULL) {
        ls_osa_free(data);
    }

    return ret;
}

irot_result_t id2_client_get_device_challenge(uint8_t* random, uint32_t* random_len)
{
    uint32_t random_str_len = 0;
    uint8_t temp[ID2_MAX_DEVICE_RANDOM_LEN];
    uint8_t *random_str = NULL;

    id2_log_debug("[id2_client_get_device_challenge enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (random == NULL || random_len == NULL || *random_len == 0) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (*random_len > ID2_MAX_DEVICE_RANDOM_LEN) {
        *random_len = ID2_MAX_DEVICE_RANDOM_LEN;
    }

    random_str_len = 2 * ID2_MAX_DEVICE_RANDOM_LEN + 1;
    random_str = ls_osa_malloc(random_str_len);
    if (random_str == NULL) {
        id2_log_error("out of mem, %d\n", random_str_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    } else {
        memset(random_str, 0, random_str_len);
    }

    id2_plat_get_random(temp, ID2_MAX_DEVICE_RANDOM_LEN);

    id2_hex_to_string(temp, ID2_MAX_DEVICE_RANDOM_LEN, random_str, &random_str_len);

    id2_log_debug("device chanllenge, %s %d\n", random_str, random_str_len);

    memcpy(random, random_str, *random_len);

    ls_osa_free(random_str);
    random_str = NULL;

    return IROT_SUCCESS;
}

irot_result_t id2_client_verify_server(
                         const uint8_t* auth_code, uint32_t auth_code_len,
                         const uint8_t* device_random, uint32_t device_random_len,
                         const uint8_t* server_extra, uint32_t server_extra_len)
{
    irot_result_t ret = IROT_ERROR_GENERIC;
    uint8_t id2_str[ID2_ID_MAX_LEN + 1] = {0};
    uint8_t auth_code_type;
    uint8_t hash_type;
    uint32_t length;
    uint32_t id2_len = ID2_ID_MAX_LEN;
    const uint8_t* head = NULL;
    const uint8_t* tail = NULL;

    uint8_t* sign_in = NULL;
    uint8_t* sign_out = NULL;
    uint32_t sign_in_len;
    uint32_t sign_out_len;

    id2_log_debug("[id2_client_verify_server enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (auth_code == NULL || auth_code_len == 0) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (device_random == NULL || device_random_len == 0) {
        id2_log_error("invalid device random.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (server_extra_len > ID2_MAX_EXTRA_LEN || (
        server_extra != NULL && server_extra_len == 0)) {
        id2_log_error("invalid server extra length, %d.\n", server_extra_len);
        return IROT_ERROR_EXCESS_DATA;
    }

    if (device_random_len > ID2_MAX_DEVICE_RANDOM_LEN) {
        id2_log_error("device random length exceed limit, %d\n", device_random_len);
        return IROT_ERROR_EXCESS_DATA;
    }

#ifdef CONFIG_ID2_AUTH_CODE_HARDEN
    if (id2_auth_code_decode((uint8_t *)auth_code, &auth_code_len) < 0) {
        id2_log_error("auth code decode error.\n");
        return IROT_ERROR_GENERIC;
    }
#endif

    id2_log_debug("server authcode: [%d] %s", auth_code_len, auth_code);

    /* auth code type */
    head = auth_code;
    auth_code_type = (head[0] - '0') * 10 + (head[1] - '0');
    if (auth_code_type != TYPE_SERVER_CHALLENGE &&
        auth_code_type != TYPE_SERVER_CHALLENGE_WITH_EXTRA) {
        id2_log_error("invalid auth code type, %d.\n", auth_code_type);
        return IROT_ERROR_GENERIC;
    }

    id2_log_hex_dump("device random", device_random, device_random_len);
    id2_log_hex_dump("server extra", server_extra, server_extra_len);
    id2_log_hex_dump("server authcode", auth_code, auth_code_len);

    /* hash type */
    head += 3;
    hash_type = *head++ - '0';
    if (hash_type != CONFIG_ID2_HASH_TYPE) {
        id2_log_error("invalid hash type, %d.\n", hash_type);
        return IROT_ERROR_GENERIC;
    }

    /* compare device random */
    head++;
    tail = (uint8_t*)strchr((const char*)head, '~');
    if (tail == NULL) {
        id2_log_error("auth code format error.\n");
        return IROT_ERROR_GENERIC;
    }

    length = tail - head;
    if (device_random_len != length) {
        id2_log_error("device random length mismatch, %d %d.\n", device_random_len, length);
        return IROT_ERROR_GENERIC;
    }

    if (memcmp(head, device_random, device_random_len) != 0) {
        id2_log_error("device random mismatch.\n");
        return IROT_ERROR_GENERIC;
    }

    ret = id2_client_get_id(id2_str, &id2_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("get ID fail, %d.\n", ret);
        goto _out;
    } else {
        /* inverse id2 */
        _id2_inverse(id2_str, id2_len);
    }

    id2_log_debug("inversed id2: %s\n", id2_str);

    /* id2 signature */
    head = tail + 1;
    sign_in_len = id2_len + device_random_len + server_extra_len;
    sign_in = (uint8_t*)ls_osa_malloc(sign_in_len + 1);
    if (sign_in == NULL) {
        id2_log_error("out of mem, %d.\n", sign_in_len + 1);
        return IROT_ERROR_OUT_OF_MEMORY;
    } else {
        memcpy(sign_in, id2_str, id2_len);
        memcpy(sign_in + id2_len, device_random, device_random_len);
    }

    if (auth_code_type == TYPE_SERVER_CHALLENGE_WITH_EXTRA) {
        if (server_extra == NULL) {
            id2_log_error("no server extra.\n");
            ret = IROT_ERROR_GENERIC;
            goto _out;
        }
        memcpy(sign_in + id2_len + device_random_len, server_extra, server_extra_len);
    }

    sign_out_len = MAX_SIGN_LEN;
    sign_out = ls_osa_malloc(sign_out_len);
    if (sign_out == NULL) {
        id2_log_error("out of mem, %d\n", sign_out_len);
        ret = IROT_ERROR_OUT_OF_MEMORY;
        goto _out;
    }

    length = auth_code_len - (head - auth_code);
    ret = id2_plat_base64_decode(head, length, sign_out, &sign_out_len);
    if (ret < 0) {
        id2_log_error("id2_plat_base64_decode fail\n");
        ret = IROT_ERROR_GENERIC;
        goto _out;
    }

    ret = _id2_verify(sign_in, sign_in_len, sign_out, sign_out_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 verify signature fail, %d\n", ret);
        goto _out;
    }

    ret = IROT_SUCCESS;

_out:
    if (sign_in) {
        ls_osa_free(sign_in);
    }
    if (sign_out) {
        ls_osa_free(sign_out);
    }

    return ret;
}

irot_result_t id2_client_get_secret(const char* seed, uint8_t* secret, uint32_t* secret_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint32_t in_len;
    uint8_t sign_buf[128];
    uint8_t out_buf[32];
    uint32_t sign_len;
    uint32_t out_len;

    id2_log_debug("[id2_client_get_secret enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (seed == NULL || secret == NULL || secret_len == NULL) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    in_len = strlen(seed);
    if (in_len == 0) {
        id2_log_error("seed is null.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }
    if (in_len > ID2_MAX_SEED_LEN) {
        id2_log_error("seed is excess data.\n");
        return IROT_ERROR_EXCESS_DATA;
    }

    if (*secret_len < ID2_DERIV_SECRET_LEN) {
        id2_log_error("short buffer, %d.\n", *secret_len);
        *secret_len = ID2_DERIV_SECRET_LEN;
        return IROT_ERROR_SHORT_BUFFER;
    }

    id2_log_info("seed: %s\n", seed);

#if (CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_3DES && \
     CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_AES  && \
     CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_SM4)
     id2_log_info("not support id2 asymmetric key type\n");
     return IROT_ERROR_NOT_SUPPORTED;
#endif

    sign_len = sizeof(sign_buf);
    ret = _id2_sign((uint8_t*)seed, in_len, sign_buf, &sign_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sign fail, %d.\n", ret);
        return ret;
    }

    out_len = sizeof(out_buf);
    ret = _id2_hash(sign_buf, sign_len, out_buf, &out_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 hash fail, %d.\n", ret);
        return ret;
    }

    id2_log_hex_dump("id2 hash output:", out_buf, out_len);

    id2_hex_to_string(out_buf, out_len, secret, secret_len);

    id2_log_debug("secret: %s\n", secret);

    return IROT_SUCCESS;
}

irot_result_t id2_client_derive_key(const char* seed, uint8_t* key, uint32_t key_len)
{
    irot_result_t ret = IROT_SUCCESS;
    uint8_t id2_str[ID2_ID_MAX_LEN + 1] = {0};
    uint8_t sign_buf[128];
    uint8_t out_buf[32];
    uint8_t *in_buf = NULL;
    uint32_t in_len;
    uint32_t out_len;
    uint32_t seed_len;
    uint32_t sign_len;
    uint32_t id_len = ID2_ID_MAX_LEN;

    id2_log_debug("[id2_client_derive_key enter.]\n");

    if (!is_id2_client_inited()) {
        id2_log_error("id2 not inited.\n");
        return IROT_ERROR_GENERIC;
    }

    if (seed == NULL || key == NULL || key_len == 0) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    if (key_len > ID2_DERIV_KEY_LEN) {
        id2_log_error("invalid key length, %d\n", key_len);
        return IROT_ERROR_BAD_PARAMETERS;
    }

    seed_len = strlen(seed);
    if (seed_len > ID2_MAX_SEED_LEN) {
        id2_log_error("seed is excess data.\n");
        return IROT_ERROR_EXCESS_DATA;
    }

    id2_log_info("seed: %s\n", seed_len == 0 ? "NULL" : seed);

#if (CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_3DES && \
     CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_AES  && \
     CONFIG_ID2_KEY_TYPE != ID2_KEY_TYPE_SM4)
     id2_log_info("not support id2 asymmetric key type\n");
     return IROT_ERROR_NOT_SUPPORTED;
#endif

    ret = id2_client_get_id(id2_str, &id_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 client get id fail, 0x%08x\n", ret);
        goto _out;
    }

    in_len = id_len + seed_len;
    in_buf = ls_osa_malloc(in_len);
    if (in_buf == NULL) {
        id2_log_error("out of mem, %d\n", in_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    } else {
        memcpy(in_buf, id2_str, id_len);
        memcpy(in_buf + id_len, seed, seed_len);
    }

    sign_len = sizeof(sign_buf);
    ret = _id2_sign(in_buf, in_len, sign_buf, &sign_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 sign fail, %d.\n", ret);
        goto _out;
    }

    out_len = sizeof(out_buf);
    ret = _id2_hash(sign_buf, sign_len, out_buf, &out_len);
    if (ret != IROT_SUCCESS) {
        id2_log_error("id2 hash fail, %d.\n", ret);
        goto _out;
    }

    id2_log_hex_dump("id2 hash output:", out_buf, out_len);

    memcpy(key, out_buf, key_len);

_out:
    if (in_buf != NULL) {
        ls_osa_free(in_buf);
    }

    return ret;
}

#if defined(CONFIG_ID2_DEBUG)
irot_result_t id2_client_set_id2_and_key(const char* id2, int key_type, const char* key_value)
{
    int ret;
    uint32_t id2_len;
    uint32_t key_len;
    uint8_t *key_data = NULL;
    km_key_data_t key_struct;
    int km_ret;

    id2_log_debug("[id2_client_set_id2_and_key enter.]\n");

    if (id2 == NULL || key_value == NULL) {
        id2_log_error("invalid input args.\n");
        return IROT_ERROR_BAD_PARAMETERS;
    }

    id2_len = strlen(id2);
    key_len = strlen(key_value);

#if (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_3DES)
    if (key_type != 0x01) {
        id2_log_error("key type error, %d\n", key_type);
        return IROT_ERROR_BAD_PARAMETERS;
    }
#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_AES)
    if (key_type != 0x02) {
        id2_log_error("key type error, %d\n", key_type);
        return IROT_ERROR_BAD_PARAMETERS;
    }
#elif (CONFIG_ID2_KEY_TYPE == ID2_KEY_TYPE_SM4)
    if (key_type != 0x03) {
        id2_log_error("key type error, %d\n", key_type);
        return IROT_ERROR_BAD_PARAMETERS;
    }
#else
    {
        id2_log_error("not support this key type, %d\n", key_type);
        return IROT_ERROR_NOT_SUPPORTED;
    }
#endif

    id2_log_info("set id2: %s\n", id2);
    id2_log_info("set key data: %s\n", key_value);
    id2_log_info("set key type: %s\n", key_type == 0x01 ? "3DES" : "AES");

    key_data = (uint8_t *)ls_osa_malloc(key_len);
    if (key_data == NULL) {
        id2_log_error("out of mem, %d\n", key_len);
        return IROT_ERROR_OUT_OF_MEMORY;
    }

    ret = id2_string_to_hex((const uint8_t*)key_value, key_len, key_data, &key_len);
    if (ret < 0) {
        id2_log_error("id2 key string to hex error.\n");
        ls_osa_free(key_data);
        return IROT_ERROR_BAD_PARAMETERS;
    }

    id2_log_hex_dump("id2_key", key_data, key_len);

    if (key_type == 0x01) {
        key_struct.type = KM_DES3;
    } else if (key_type == 0x02) {
        key_struct.type = KM_AES;
    } else if (key_type == 0x03) {
        key_struct.type = KM_SM4;
    }

    key_struct.sym_key.key_bit = key_len << 3;
    key_struct.sym_key.key = key_data;

    km_delete_key(ID2_KEY_NAME, ID2_KEY_NAME_LEN);

    km_ret = km_set_id2((uint8_t*)id2, id2_len);
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km set id2 error, 0x%08x\n", km_ret);
        ls_osa_free(key_data);
        return IROT_ERROR_GENERIC;
    }

    km_ret = km_import_key(ID2_KEY_NAME, ID2_KEY_NAME_LEN,
                KM_KEY_FORMAT_RAW, &key_struct, sizeof(key_struct));
    if (km_ret != KM_SUCCESS) {
        id2_log_error("km import key error, 0x%08x\n", km_ret);
        ls_osa_free(key_data);
        return IROT_ERROR_GENERIC;
    }

    id2_log_info("set id2 and key success!\n");

    s_id2_id_len = 0;

    ls_osa_free(key_data);

    return IROT_SUCCESS;
}

#else

irot_result_t id2_client_set_id2_and_key(const char* id2, int key_type, const char* key_value)
{
    (void)id2;
    (void)key_type;
    (void)key_value;

    id2_log_info("not supported\n");

    return IROT_ERROR_NOT_SUPPORTED;
}

#endif

