#include "dk_scp_common.h"
#include "dk_crypto.h"
#include "dk_utils.h"

#include <stdio.h>

#ifdef DK_DEBUG
DK_Result (*test_scp_generate_host_challenge_ptr)(scp_context *ctx) = NULL;
#endif

byte LABEL_DERIVATION_SENC[KDF_LABEL_SIZE]  = {0,0,0,0,0,0,0,0,0,0,0,0x04};
byte LABEL_DERIVATION_SMAC[KDF_LABEL_SIZE]  = {0,0,0,0,0,0,0,0,0,0,0,0x06};
byte LABEL_DERIVATION_SRMAC[KDF_LABEL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0x07};
byte LABEL_DERIVATION_CARD_CRYPTOGRAM[KDF_LABEL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0x00};
byte LABEL_DERIVATION_HOST_CRYPTOGRAM[KDF_LABEL_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0x01};

static scp_context scp_context_default = {0};

DK_Result scp_init(scp_context* ctx)
{
    dk_memcpy(ctx, &scp_context_default, sizeof(scp_context));

    ctx->session_state = SESSION_CLOSED;

    return DK_SUCCESS;
}

DK_Result scp_clear(scp_context *ctx)
{
    if (ctx->key_enc) { 
        dk_memset(ctx->key_enc, 0, ctx->key_enc_len);
        dk_free(ctx->key_enc); 
    }

    if (ctx->key_mac) { 
        dk_memset(ctx->key_mac, 0, ctx->key_mac_len);
        dk_free(ctx->key_mac); 
    }

    if (ctx->senc) { 
        dk_memset(ctx->senc, 0, ctx->senc_len);
        dk_free(ctx->senc); 
    }

    if (ctx->smac) { 
        dk_memset(ctx->smac, 0, ctx->smac_len);
        dk_free(ctx->smac); 
    }

    if (ctx->srmac) { 
        dk_memset(ctx->srmac, 0, ctx->srmac_len);
        dk_free(ctx->srmac); 
    }


    return scp_init(ctx);
}

DK_Result scp_calculate_rmac(scp_context* ctx, byte* rdf, size_t rdf_len, byte* sw, size_t sw_len, byte* rmac)
{
    byte mac[MAC_CHAINING_SIZE];
    DK_Result rc = DK_SUCCESS;

    rc = calculate_cmac(ctx->srmac, ctx->srmac_len, mac, MAC_CHAINING_SIZE, 3, 
                        ctx->chaining, MAC_CHAINING_SIZE, rdf, rdf_len, sw, sw_len);
    if (rc)
        return rc;
    
    dk_memcpy(rmac, mac, R_MAC_SIZE);

    return DK_SUCCESS;
}

DK_Result scp_generate_session_keys(scp_context* ctx)
{
    DK_Result rc;
    byte* context = ARRAY_CONCAT(byte, ctx->host_challenge, CHALLENGE_SIZE, ctx->card_challenge, CHALLENGE_SIZE);
    size_t context_len = 2 * CHALLENGE_SIZE;

    byte* senc;
    byte* smac;
    byte* srmac;

    rc = kdf_counter(ctx->key_enc, ctx->key_enc_len, 
                     LABEL_DERIVATION_SENC, KDF_LABEL_SIZE, 
                     context, context_len, ctx->key_enc_len * 8, &(senc));

    if (rc) {
        goto catch_error;
    }

    rc = kdf_counter(ctx->key_mac, ctx->key_mac_len, 
                     LABEL_DERIVATION_SMAC, KDF_LABEL_SIZE, 
                     context, context_len, ctx->key_mac_len * 8, &(smac));
    if (rc) {
        goto catch_error;
    }

    rc = kdf_counter(ctx->key_mac, ctx->key_mac_len, 
                     LABEL_DERIVATION_SRMAC, KDF_LABEL_SIZE, 
                     context, context_len, ctx->key_mac_len * 8, &(srmac));
    if (rc) {
        goto catch_error;
    }

    ctx->senc = senc;
    ctx->senc_len = ctx->key_enc_len;

    ctx->smac = smac;
    ctx->smac_len = ctx->key_mac_len;

    ctx->srmac = srmac;
    ctx->srmac_len = ctx->key_mac_len;

    ctx->session_state = SESSION_OPEN;

    rc = DK_SUCCESS;
    goto success;

catch_error:
    if (senc) dk_free(senc);
    if (smac) dk_free(smac);
    if (srmac) dk_free(srmac);

success:
    if (context) dk_free(context);
    return rc;
        
}

DK_Result scp_generate_card_challenge(scp_context *ctx)
{   
    DK_Result rc;
    byte challenge[CHALLENGE_SIZE];

    TEE_GenerateRandom(challenge, CHALLENGE_SIZE);

    dk_memcpy(ctx->card_challenge, challenge, CHALLENGE_SIZE);

    rc = DK_SUCCESS;

catch_error:
    return rc;
}

DK_Result scp_generate_card_cryptogram(scp_context* ctx, byte* cryptogram)
{
    DK_Result rc;
    byte* context = ARRAY_CONCAT(byte, ctx->host_challenge, CHALLENGE_SIZE, ctx->card_challenge, CHALLENGE_SIZE);
    size_t context_len = 2 * CHALLENGE_SIZE;

    byte* res;

    rc = kdf_counter(ctx->smac, ctx->smac_len, 
                     LABEL_DERIVATION_CARD_CRYPTOGRAM, KDF_LABEL_SIZE, 
                     context, context_len, KDF_COUNTER_L_64, &(res));
    if (rc) {
        goto catch_error;
    }

    dk_memcpy(cryptogram, res, CRYPTOGRAM_SIZE);

    rc = DK_SUCCESS;
    goto success;

catch_error:
    if (res) dk_free(res);

success:
    if (context) dk_free(context);
    return rc;
}

DK_Result scp_verify_card_cryptogram(scp_context* ctx, byte* cryptogram)
{
    DK_Result rc;

    byte crypt[CRYPTOGRAM_SIZE];

    rc = scp_generate_card_cryptogram(ctx, crypt);
    if (rc)
        goto catch_error;

    if (0 != dk_memcmp(cryptogram, crypt, CRYPTOGRAM_SIZE)) {
        rc = DK_ERROR_GENERIC;
        goto catch_error;
    }

    rc = DK_SUCCESS;

catch_error:
    return rc;
}

#ifdef DK_DEBUG
DK_Result __scp_generate_host_challenge(scp_context *ctx)
#else
DK_Result scp_generate_host_challenge(scp_context *ctx)
#endif
{   
    DK_Result rc;
    byte challenge[CHALLENGE_SIZE];

    TEE_GenerateRandom(challenge, CHALLENGE_SIZE);

    dk_memcpy(ctx->host_challenge, challenge, CHALLENGE_SIZE);

    rc = DK_SUCCESS;

catch_error:
    return rc;
}

#ifdef DK_DEBUG
DK_Result scp_generate_host_challenge(scp_context *ctx)
{
    if (test_scp_generate_host_challenge_ptr) return test_scp_generate_host_challenge_ptr(ctx);
    else return __scp_generate_host_challenge(ctx);
}

DK_Result mock_scp_generate_host_challenge_test_open_secure_channel(scp_context *ctx)
{
    byte host_challenge[] = {(byte)0xf2, (byte)0x6d, (byte)0x9a, (byte)0x1f, 
                             (byte)0x21, (byte)0x67, (byte)0x46, (byte)0xd3};
    dk_memcpy(ctx->host_challenge, host_challenge, sizeof(host_challenge));
    return DK_SUCCESS;
}

DK_Result mock_scp_generate_host_challenge_test_generate_init_update(scp_context *ctx)
{   
    DK_Result rc;

    byte host_challenge[] = {(byte)0xf2, (byte)0x6d, (byte)0x9a, (byte)0x1f, 
                             (byte)0x21, (byte)0x67, (byte)0x46, (byte)0xd3};

    if (ctx != NULL) {
        dk_memcpy(ctx->host_challenge, host_challenge, sizeof(host_challenge));
    }
    else {
        rc = DK_ERROR_GENERIC;
        goto catch_error;
    }

    rc = DK_SUCCESS;
catch_error:
    return rc;
}

DK_Result mock_scp_generate_host_challenge_test_full(scp_context *ctx)
{
    byte host_challenge[] = {
        (byte)0xf2, (byte)0x6d, (byte)0x9a, (byte)0x1f, 
        (byte)0x21, (byte)0x67, (byte)0x46, (byte)0xd3};

    dk_memcpy(ctx->host_challenge, host_challenge, sizeof(host_challenge));
    return DK_SUCCESS;
}
#endif

DK_Result scp_generate_host_cryptogram(scp_context* ctx, byte* cryptogram)
{
    DK_Result rc;
    byte* context = ARRAY_CONCAT(byte, ctx->host_challenge, CHALLENGE_SIZE, ctx->card_challenge, CHALLENGE_SIZE);
    size_t context_len = 2 * CHALLENGE_SIZE;

    byte* res;

    rc = kdf_counter(ctx->smac, ctx->smac_len, 
                     LABEL_DERIVATION_HOST_CRYPTOGRAM, KDF_LABEL_SIZE, 
                     context, context_len, KDF_COUNTER_L_64, &(res));
    if (rc) {
        goto catch_error;
    }

    dk_memcpy(cryptogram, res, CRYPTOGRAM_SIZE);

    rc = DK_SUCCESS;
    goto success;

catch_error:
    if (res) dk_free(res);

success:
    if (context) dk_free(context);
    return rc;
}

DK_Result scp_left_pad(byte* data, size_t data_len, byte** out, size_t* out_len)
{
    size_t to_add;

    if (data == NULL)
        return DK_ERROR_NO_DATA;

    if (data_len % AES_BLOCK_SIZE == 0) {
        *out = dk_malloc(data_len);
        *out_len = data_len;
        dk_memcpy(*out, data, data_len);
    }
    else {
        to_add = AES_BLOCK_SIZE - (data_len % AES_BLOCK_SIZE);
        *out = dk_malloc(data_len + to_add);
        *out_len = data_len + to_add;
        dk_memcpy((*out)+to_add, data, data_len);
    }
         
    return DK_SUCCESS;         
}

DK_Result scp_aes_pad(byte* data, size_t data_len, byte* out, size_t* out_len)
{
    // should this be inplace? what are the implications for data consistency?
    size_t to_add;
    size_t len = data_len + 1;

    if (data == NULL || out == NULL || *out_len < data_len + AES_BLOCK_SIZE) 
        return DK_ERROR_NO_DATA;

    to_add = AES_BLOCK_SIZE - (len % AES_BLOCK_SIZE);
    *out_len = len + to_add;
    dk_memcpy(out, data, data_len);
    dk_memset(out+len, 0, to_add);
    out[len-1] = PADDING_DELIMITER;
        
    return DK_SUCCESS;     
}

DK_Result scp_strip_aes_pad(byte *data, size_t data_len, byte* out, size_t* out_len)
{
    size_t count;

    if (data == NULL || out == NULL || *out_len < data_len - AES_BLOCK_SIZE) 
        return DK_ERROR_NO_DATA;

    count = data_len - 1;
    while (count > 0 && data[count] == 0) 
        count--;
    
    if (data[count] != (byte)0x80)
        return DK_ERROR_GENERIC;

    //count--; // remove 0x80

    *out_len = count;
    dk_memcpy(out, data, *out_len);

    return DK_SUCCESS;
}

DK_Result scp_generate_icv(scp_context* ctx, byte* iv, byte is_response)
{
    DK_Result rc;
    byte iv_zero[AES_BLOCK_SIZE];
    byte res[AES_BLOCK_SIZE];
    size_t iv_len = AES_BLOCK_SIZE;

    if (iv == NULL)
        return DK_ERROR_BAD_PARAM;

    dk_memset(iv_zero, 0, AES_BLOCK_SIZE);
    dk_memset(res, 0, AES_BLOCK_SIZE);

    size_t s = sizeof(unsigned long);
    long_to_byte_array(ctx->encryption_counter, res + AES_BLOCK_SIZE - s, s);
    if (is_response) res[0] = 0x80;

    rc = aes_encrypt(res, AES_BLOCK_SIZE, ctx->senc, ctx->senc_len, iv_zero, AES_BLOCK_SIZE, res, &iv_len);
    if (rc)
        goto catch_error;

    if (iv_len != AES_BLOCK_SIZE) {
        rc = DK_ERROR_BAD_STATE;
        goto catch_error;
    }

    dk_memcpy(iv, res, AES_BLOCK_SIZE);

    rc = DK_SUCCESS;

catch_error:
    return rc;
}

DK_Result scp_generate_command_icv(scp_context* ctx, byte* iv)
{
    return scp_generate_icv(ctx, iv, 0);
}

DK_Result scp_generate_response_icv(scp_context* ctx, byte* iv)
{
   return scp_generate_icv(ctx, iv, 1);
}

DK_Result scp_wrap_command(scp_context *ctx, byte *apdu, size_t apdu_len, byte *wrapped_apdu, size_t *wrapped_apdu_len)
{
    DK_Result rc;

    size_t current_len;

    // wrapped apdu buffer must be preallocated with CPDU_MAX_SIZE bytes
    // actual final size will be determined after encryption due to padding
    // but final result will be the same
    if (apdu == NULL || apdu_len < 6 || 
        wrapped_apdu == NULL) {
        return DK_ERROR_BAD_PARAM;
    }

    // TODO: maybe create another function called "wrap_not_in_place" where this is done instead
    // when we want to put results in a different buffer
    dk_memcpy(wrapped_apdu, apdu, apdu_len);
    *wrapped_apdu_len = apdu_len;
    current_len = CPDU_MAX_SIZE;


    if (ctx->security_level & SCP03_SEC_AUTHENTICATED) {
        if (ctx->security_level & SCP03_SEC_C_DECRYPTION) {
            rc = scp_encrypt_command(ctx, apdu, apdu_len, wrapped_apdu, &current_len);
            if (rc) {
                DK_LOG_ERR("scp_wrap_command: bad encrypt");
                goto catch_error;
            }

            *wrapped_apdu_len = current_len;
            current_len = CPDU_MAX_SIZE;
        }
        if (ctx->security_level & SCP03_SEC_C_MAC) {
            rc = scp_generate_apdu_cmac(ctx, wrapped_apdu, *wrapped_apdu_len, wrapped_apdu, &current_len);
            if (rc) {
                DK_LOG_ERR("scp_wrap_command: bad cmac");
                goto catch_error;
            }
            *wrapped_apdu_len = current_len;
        }
    }

    rc = DK_SUCCESS;

catch_error:
    return rc;
}

DK_Result scp_encrypt_command(scp_context *ctx, byte *apdu, size_t apdu_len, byte *wrapped_apdu, size_t *wrapped_apdu_len)
{
    DK_Result rc;

    // byte data[CPDU_MAX_DATA_SIZE]; 
    byte padded_data[CPDU_MAX_DATA_SIZE];
    // byte encrypted_data[CPDU_MAX_DATA_SIZE];

    size_t data_len = CPDU_MAX_DATA_SIZE; 
    size_t padded_data_len = CPDU_MAX_DATA_SIZE;
    size_t encrypted_data_len = CPDU_MAX_DATA_SIZE;

    byte iv[AES_BLOCK_SIZE];

    size_t max_cdata_size = CPDU_MAX_DATA_SIZE;

    // wrapped apdu buffer must be preallocated with CPDU_MAX_SIZE bytes;
    // actual final size will be determined after encryption due to padding,
    // but final result will be the same

    if (apdu == NULL || apdu_len < 6 || 
        wrapped_apdu == NULL || *wrapped_apdu_len < CPDU_MAX_SIZE) {
        return DK_ERROR_BAD_PARAM;
    }

    if (ctx->security_level & SCP03_SEC_C_MAC)
        max_cdata_size -= C_MAC_SIZE;

    data_len = apdu[APDU_OFFSET_LC];
    if (data_len == 0) {
        return DK_ERROR_BAD_PARAM;
    }

    // dk_memcpy(data, apdu + APDU_OFFSET_CDATA, data_len);

    rc = scp_aes_pad(apdu + APDU_OFFSET_CDATA, data_len, padded_data, &padded_data_len);
    if (rc)
        goto catch_error;

    if (padded_data_len > max_cdata_size) {
        rc = DK_ERROR_EXCESS_DATA;
        goto catch_error;
    }

    rc = scp_generate_command_icv(ctx, iv);
    if (rc) 
        goto catch_error;

    rc = aes_encrypt(padded_data, padded_data_len, ctx->senc, ctx->senc_len, 
                     iv, AES_BLOCK_SIZE, wrapped_apdu + APDU_OFFSET_CDATA, &encrypted_data_len);
    if (rc) 
        goto catch_error;

    dk_memcpy(wrapped_apdu, apdu, APDU_OFFSET_LC);
    wrapped_apdu[APDU_OFFSET_LC] = encrypted_data_len;
    // dk_memcpy(wrapped_apdu + APDU_OFFSET_CDATA, encrypted_data, encrypted_data_len);

    *wrapped_apdu_len = APDU_OFFSET_CDATA + encrypted_data_len;

    rc = DK_SUCCESS;

catch_error:
    // dk_memset(data, 0, CPDU_MAX_DATA_SIZE);
    dk_memset(padded_data, 0, CPDU_MAX_DATA_SIZE);
    // dk_memset(encrypted_data, 0, CPDU_MAX_DATA_SIZE);

    return rc;
}

DK_Result scp_generate_apdu_cmac(scp_context *ctx, byte *apdu, size_t apdu_len, byte *wrapped_apdu, size_t *wrapped_apdu_len)
{
    DK_Result rc;

    byte res[CPDU_MAX_SIZE];
    byte mac[MAC_CHAINING_SIZE];

    size_t lc;
    size_t res_len;

    // wrapped apdu buffer must be preallocated with CPDU_MAX_SIZE bytes
    // actual final size will be determined after encryption due to padding
    // but final result will be the same
    if (apdu == NULL || apdu_len < 6 || 
        wrapped_apdu == NULL || *wrapped_apdu_len < CPDU_MAX_SIZE) {
            return DK_ERROR_BAD_PARAM;
    }

    lc = apdu[APDU_OFFSET_LC];
    res_len = apdu_len + C_MAC_SIZE;

    dk_memcpy(res, apdu, APDU_OFFSET_CDATA + lc);

    res[APDU_OFFSET_LC]  += C_MAC_SIZE;
    res[APDU_OFFSET_CLA] &= LOGICAL_CHANNEL_ZERO_MASK;
    res[APDU_OFFSET_CLA] &= BIT4_ZERO_MASK;
    res[APDU_OFFSET_CLA] |= BIT3_MASK;

    rc = calculate_cmac(ctx->smac, ctx->smac_len, mac, MAC_CHAINING_SIZE, 
                        2, ctx->chaining, MAC_CHAINING_SIZE, res, APDU_OFFSET_CDATA + lc);
    if (rc) {
        goto catch_error;
    }

    dk_memcpy(res + APDU_OFFSET_CDATA + lc, mac, C_MAC_SIZE);
    if (APDU_OFFSET_CDATA + lc < apdu_len) {
        dk_memcpy(res + (APDU_OFFSET_CDATA + lc) + C_MAC_SIZE, apdu + (APDU_OFFSET_CDATA + lc), apdu_len - (APDU_OFFSET_CDATA + lc));
    }

    dk_memcpy(ctx->chaining, mac, MAC_CHAINING_SIZE);

    dk_memcpy(wrapped_apdu, res, res_len);
    *wrapped_apdu_len = res_len;

    rc = DK_SUCCESS;

catch_error:
    dk_memset(res, 0, CPDU_MAX_SIZE);
    dk_memset(mac, 0, MAC_CHAINING_SIZE);

    return rc;
}

DK_Result scp_unwrap_response(scp_context *ctx, byte *wrapped_apdu, size_t wrapped_apdu_len, byte *apdu, size_t *apdu_len)
{
    DK_Result rc;
    size_t current_len;

    if (wrapped_apdu == NULL || wrapped_apdu_len < 6 || apdu == NULL) {
        return DK_ERROR_BAD_PARAM;
    }

    dk_memcpy(apdu, wrapped_apdu, wrapped_apdu_len);
    *apdu_len = wrapped_apdu_len;
    current_len = RPDU_MAX_SIZE;

    if (ctx->security_level & SCP03_SEC_AUTHENTICATED) {
        if (ctx->security_level & SCP03_SEC_R_MAC) {
            rc = scp_verify_apdu_rmac(ctx, wrapped_apdu, wrapped_apdu_len);
            if (rc) {
                goto catch_error;
            }
        }

        if (ctx->security_level & SCP03_SEC_R_ENCRYPTION) {
            rc = scp_decrypt_response(ctx, wrapped_apdu, wrapped_apdu_len, apdu, &current_len);
            if (rc) {
                goto catch_error;
            }

            *apdu_len = current_len;
        }
    }

catch_error:
    return rc;
}

DK_Result scp_verify_apdu_rmac(scp_context *ctx, byte* apdu, size_t apdu_len)
{
    DK_Result rc;
    
    // byte rdf[CPDU_MAX_DATA_SIZE];
    // byte sw[APDU_SW_SIZE];
    byte rmac[R_MAC_SIZE];
    
    size_t rdf_len;
    size_t sw_offset;

    if (apdu == NULL)
        return DK_ERROR_BAD_PARAM;

    rdf_len = apdu_len - R_MAC_SIZE - APDU_SW_SIZE;
    sw_offset = apdu_len - APDU_SW_SIZE;

    // dk_memcpy(rdf, apdu, rdf_len);
    // dk_memcpy(sw, apdu + rdf_len + R_MAC_SIZE, APDU_SW_SIZE);

    scp_calculate_rmac(ctx, apdu, rdf_len, apdu + sw_offset, APDU_SW_SIZE, rmac);

    if (0 != dk_memcmp(rmac, apdu + rdf_len, R_MAC_SIZE)) {
        rc = DK_ERROR_GENERIC;
        goto catch_error;
    }

    rc = DK_SUCCESS;

catch_error:
    // dk_memset(rdf, 0, rdf_len);
    // dk_memset(sw, 0, APDU_SW_SIZE);
    dk_memset(rmac, 0, R_MAC_SIZE);

    return rc;
}

DK_Result scp_decrypt_response(scp_context *ctx, byte* wrapped_apdu, size_t wrapped_apdu_len, byte* apdu, size_t *apdu_len)
{
    DK_Result rc;

    byte sw[APDU_SW_SIZE];
    // byte data[CPDU_MAX_DATA_SIZE];
    // byte plaintext[CPDU_MAX_DATA_SIZE];
    byte iv[AES_BLOCK_SIZE];
    // byte stripped[CPDU_MAX_DATA_SIZE];

    size_t data_len;
    // size_t plaintext_len = CPDU_MAX_DATA_SIZE;
    // size_t stripped_len;
    size_t offset;

    size_t sw_offset;

    if (wrapped_apdu == NULL || wrapped_apdu_len < 6 || apdu == NULL) {
        return DK_ERROR_BAD_PARAM;
    }

    data_len = wrapped_apdu_len - APDU_SW_SIZE;

    // dk_memcpy(data, wrapped_apdu, wrapped_apdu_len - APDU_SW_SIZE);
    dk_memcpy(sw, wrapped_apdu + wrapped_apdu_len - APDU_SW_SIZE, APDU_SW_SIZE);

    // data_len = wrapped_apdu_len - APDU_SW_SIZE;
    if (ctx->security_level & SCP03_SEC_R_MAC) {
        data_len -= R_MAC_SIZE;
    }

    rc = scp_generate_response_icv(ctx, iv);
    if (rc)
        goto catch_error;

    rc = aes_decrypt(wrapped_apdu, data_len, ctx->senc, ctx->senc_len, iv, AES_BLOCK_SIZE, apdu, apdu_len);
    if (rc)
        goto catch_error;

    rc = scp_strip_aes_pad(apdu, *apdu_len, apdu, apdu_len);
    if (rc)
        goto catch_error;

    // dk_memcpy(apdu, stripped, stripped_len);
    dk_memcpy(apdu + *apdu_len, sw, APDU_SW_SIZE);

    *apdu_len = *apdu_len + APDU_SW_SIZE;


catch_error:
    // dk_memset(stripped, 0, stripped_len);
    dk_memset(iv, 0, AES_BLOCK_SIZE);
    // dk_memset(plaintext, 0, CPDU_MAX_DATA_SIZE);
    // dk_memset(data, 0, CPDU_MAX_DATA_SIZE);
    dk_memset(sw, 0, APDU_SW_SIZE);

    return rc;
}

#ifdef DK_DEBUG
void scp_dump_context(scp_context *ctx)
{
    DK_LOG_INFO("******************dumping context******************");

    printf(TAG"  key_enc[%d]: ", ctx->key_enc_len);
    print_array(ctx->key_enc, ctx->key_enc_len);

    printf(TAG"  key_mac[%d]: ", ctx->key_mac_len);
    print_array(ctx->key_mac, ctx->key_mac_len);

    printf(TAG"  senc[%d]: ", ctx->senc_len);
    print_array(ctx->senc, ctx->senc_len);

    printf(TAG"  smac[%d]: ", ctx->smac_len);
    print_array(ctx->smac, ctx->smac_len);

    printf(TAG"  srmac[%d]: ", ctx->srmac_len);
    print_array(ctx->srmac, ctx->srmac_len);

    printf(TAG"  encryption_counter: %d\n", ctx->encryption_counter);
    printf(TAG"  security_level: %x\n", ctx->security_level);

    printf(TAG"  key_information[%d]: ", KEY_INFO_SIZE);
    print_array(ctx->key_information, KEY_INFO_SIZE);

    printf(TAG"  byteI: %x\n", ctx->byteI);

    printf(TAG"  chaining[%d]: ", MAC_CHAINING_SIZE);
    print_array(ctx->chaining, MAC_CHAINING_SIZE);

    printf(TAG"  host_challenge[%d]: ", CHALLENGE_SIZE);
    print_array(ctx->host_challenge, CHALLENGE_SIZE);

    printf(TAG"  card_challenge[%d]: ", CHALLENGE_SIZE);
    print_array(ctx->card_challenge, CHALLENGE_SIZE);

    printf(TAG"  host_cryptogram[%d]: ", CRYPTOGRAM_SIZE);
    print_array(ctx->host_cryptogram, CRYPTOGRAM_SIZE);

    printf(TAG"  card_cryptogram[%d]: ", CRYPTOGRAM_SIZE);
    print_array(ctx->card_cryptogram, CRYPTOGRAM_SIZE);

    printf(TAG"  session_state: %x\n", ctx->session_state);

    DK_LOG_INFO("***************************************************");
}
#endif