// SPDX-License-Identifier: BSD-3-Clause
/*
 * Copyright 2018-2020, 2022-2025 NXP
 */

/*===========================================================================*/
/**
    @file    acst.c

    @brief   Code signing tool's AHAB file, handles AHAB data and creates
             output binary with the source binary, certificates and signatures
             generated while processing csf commands.
 */

/*===========================================================================
                                INCLUDE FILES
=============================================================================*/
#include <string.h>
#include <time.h>
#include <openssl/x509v3.h>
#include "err.h"
#include "srk_helper.h"
#include "misc_helper.h"

/*===========================================================================
                               LOCAL CONSTANTS
=============================================================================*/
#define MAX_ERR_MSG_BYTES (1024)
#define FILE_EXT_BIN      ".bin"

/*===========================================================================
                                 LOCAL MACROS
=============================================================================*/
#define ALIGN(val,mask)   (((val)+((mask)-1))&~((mask)-1))

/*===========================================================================
                  LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
=============================================================================*/

/*===========================================================================
                            LOCAL VARIABLES
=============================================================================*/
static char err_msg[MAX_ERR_MSG_BYTES];

/*===========================================================================
                            LOCAL FUNCTION PROTOTYPES
=============================================================================*/
/** Retrieve hash algo
 *
 * Reads an SRK table to retrieve the hash algo information
 *
 * @param[in]  srk_table Byte string of a SRK table
 *
 * @pre @a srk_table must not be NULL
 *
 * @returns the hash algo information
 */
static hash_alg_t
get_hash_alg(byte_str_t *srk_table);

/** Get signature size
 *
 * Computes the expected signature structure/data size based on the key
 * information
 *
 * @param[in]  filename Certificate file
 *
 * @pre @a filename must not be NULL
 *
 * @returns the size of the signature generated by using this key pair
 */
static size_t get_signature_structure_size(const char *filename);

/** Convert certificate to NXP format
 *
 * Converts a certificate to a Certificate struct as defined by NXP
 *
 * @param[in]  filename       Certificate file
 *
 * @param[out] cert_nxp       Certificate output in NXP format
 *
 * @param[in]  permissions    Permissions specified for this certificate
 *
 * @param[in]  permissions_data  Complementary informations for debug auth feature
 *
 * @param[in]  uuid  Unique ID of the targetted device (NXP Certs)
 *
 * @param[in]  fuse_version  Version of certificate
 *
 * @param[in]  hash           Hash algo to be used for signature generation
 *
 * @param[in]  signature_size Size of the certificate signature
 *
 * @pre @a filename and @a cert_nxp must not be NULL
 *
 * @rpost none
 */
static void convert_to_nxp_format(const char *filename, byte_str_t *cert_nxp,
                                  uint8_t permissions,
                                  triple_t permissions_data, quadruple_t uuid,
                                  uint8_t fuse_version, hash_alg_t hash,
                                  size_t signature_size);

/** Convert data to file
 *
 * Writes a byte string to an output file
 *
 * @param[in]  data     Input byte string
 *
 * @param[in]  filename Output file
 *
 * @pre @a data and @a filename must not be NULL
 *
 * @post none
 */
static void
convert_byte_str_to_file(byte_str_t *data, const char *filename);

/** Generate a signature
 *
 * Generates a signature over the input data
 *
 * @param[in]  data Input byte string to be signed
 *
 * @param[in]  key  Certificate used to sign
 *
 * @param[in]  hash Hash algo used to sign
 *
 * @param[out] sign Generated signature
 *
 * @pre @a data, @a key and @a sign must not be NULL
 *
 * @post none
 */
static void
generate_signature(byte_str_t *data,
                   const char *data_filename_prefix,
                   const char *key,
                   hash_alg_t hash,
                   byte_str_t *sign,
                   const char *sign_filename);

/** Generate the final output
 *
 * Generates the ouput file expected by the user
 *
 * @param[in]  src     Unsigned source file
 *
 * @param[in]  data    Signed container
 *
 * @param[in]  offsets Where to find the container in the source file
 *
 * @param[in]  dst     Signed destination file
 *
 * @pre @a src, @a data and @a offsets must not be NULL
 *
 * @post Program exits with exit code 0 on success.
 */
static void
generate_output(FILE       *dst_tmp,
                byte_str_t *data,
                offsets_t  *offsets,
                const char *dst);

/*===========================================================================
                            LOCAL FUNCTIONS
=============================================================================*/
/*--------------------------
  ahab_hash_2_cst_hash
---------------------------*/
hash_alg_t ahab_hash_2_cst_hash(uint8_t h)
{
    switch (h)
    {
        case HASH_SHA256:
            return SHA_256;
        case HASH_SHA384:
            return SHA_384;
        case HASH_SHA512:
            return SHA_512;
        case HASH_SHA3_256:
            return SHA3_256;
        case HASH_SHA3_384:
            return SHA3_384;
        case HASH_SHA3_512:
            return SHA3_512;
        case HASH_SHAKE128_256:
            return SHAKE128_256;
        case HASH_SHAKE256_512:
            return SHAKE256_512;
        default:
            return INVALID_DIGEST;
    }
}

/*--------------------------
  get_hash_alg
---------------------------*/
hash_alg_t get_hash_alg(byte_str_t *srk_table)
{
    uint32_t srk_arr_hdr_bytes =
        sizeof(struct ahab_container_srk_table_array_s);
    uint32_t srk_tab_hdr_bytes = sizeof(struct ahab_container_srk_table_s);
    uint32_t srk_rec_hdr_bytes = sizeof(struct ahab_container_srk_s);
    size_t base_offset = srk_tab_hdr_bytes;

    /* Check overflow */
    if ((srk_tab_hdr_bytes + srk_rec_hdr_bytes) < srk_tab_hdr_bytes)
    {
        error("Wrong srk struct definitions");
    }

    /* Check if enough data */
    if (srk_table->entry_bytes < (srk_tab_hdr_bytes + srk_rec_hdr_bytes))
    {
        error("SRK table too small");
    }

    /* Get hash algo info from SRK record 0                   */
    /* For AHAB, all SRKs in a table must be of the same type */
    if (IS_AHAB_2(g_target))
    {
        base_offset += srk_arr_hdr_bytes;
    }

    return ahab_hash_2_cst_hash(
        ((struct ahab_container_srk_s *) (srk_table->entry + base_offset))
            ->hash_alg);
}

/*--------------------------
  get_signature_structure_size
---------------------------*/
size_t get_signature_structure_size(const char *filename)
{
    X509     *cert = NULL;
    EVP_PKEY *pkey = NULL;
    size_t   size  = offsetof(struct ahab_container_signature_s, data);
    size_t sig0_size = 0;
    size_t sig1_size = 0;

    /* Open the certificate file */
    cert = read_certificate(filename);
    if (NULL == cert)
    {
        snprintf(err_msg, MAX_ERR_MSG_BYTES, "Unable to read %s", filename);
        error(err_msg);
    }

    /* Extract public key information */
    pkey = X509_get_pubkey(cert);
    if (NULL == pkey)
    {
        X509_free(cert);
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to retrieve the public key from %s",
                 filename);
        error(err_msg);
    }

    /* Compute the size of the signature generated by using this key pair */
    if (get_signature_size(pkey, &sig0_size, &sig1_size) != 1)
    {
        error("Error getting signature size");
    }

    if (sig1_size > 0)
    {
        /* hybrid signatures data */
        size *= CNTN_MAX_NR_SRK_TABLE;
        size += sig1_size;
    }

    size += sig0_size;

    EVP_PKEY_free(pkey);
    X509_free(cert);

    return size;
}

/*--------------------------
  convert_to_nxp_format
---------------------------*/
void convert_to_nxp_format(const char *filename, byte_str_t *cert_nxp,
                           uint8_t permissions, triple_t permissions_data,
                           quadruple_t uuid, uint8_t fuse_version,
                           hash_alg_t hash, size_t signature_size)
{
    srk_entry_t *srk_entry[CNTN_MAX_NR_SRK_TABLE] = {NULL, NULL};
    ahab_container_certificate_v1_t *ahab_v1_cert = NULL;
    ahab_container_certificate_v2_t *ahab_v2_cert = NULL;
    uint32_t cert_hdr_bytes = 0;
    uint8_t *srk_data[CNTN_MAX_NR_SRK_TABLE] = {NULL, NULL};
    size_t srk_data_size[CNTN_MAX_NR_SRK_TABLE] = {0, 0};
    int is_hybrid = -1;
    size_t num_records = 1;
    size_t i = 0;
    size_t offset = 0;

    if (IS_AHAB_1(g_target))
    {
        /* Build the SRK record */
        srk_entry[0] = cert_to_srk_entry(g_target, filename, 0, 0,
                                         get_digest_name(hash), false);
        if (!srk_entry[0])
        {
            error("Cannot build an SRK entry from the certificate");
        }

        /* Build the complete certificate */
        cert_hdr_bytes = offsetof(ahab_container_certificate_v1_t, public_key);
        /* The Certificate signature must start on 64-bit boundaries */
        cert_nxp->entry_bytes =
            ALIGN(cert_hdr_bytes + srk_entry[0]->entry_bytes, 8);
        cert_nxp->entry = malloc(cert_nxp->entry_bytes);
        if (NULL == cert_nxp->entry)
        {
            free(srk_entry[0]->entry);
            error("Cannot allocate buffer fro handling the \'Certificate\'");
        }

        memset(cert_nxp->entry, 0, cert_nxp->entry_bytes);

        /* Check overflow */
        if ((signature_size + cert_nxp->entry_bytes) < signature_size)
        {
            free(srk_entry[0]->entry);
            error("Wrong \'Certificate\' or \'Signature\' definitions");
        }

        ahab_v1_cert = (ahab_container_certificate_v1_t *) (cert_nxp->entry);
        ahab_v1_cert->tag = CERTIFICATE_TAG;
        ahab_v1_cert->version = CERTIFICATE_VERSION_1;
        ahab_v1_cert->length = cert_nxp->entry_bytes + signature_size;
        ahab_v1_cert->signature_offset = cert_nxp->entry_bytes;

        cert_nxp->entry[6] = ~permissions;
        cert_nxp->entry[7] = permissions;

        memcpy(cert_nxp->entry + cert_hdr_bytes, srk_entry[0]->entry,
               srk_entry[0]->entry_bytes);

        free(srk_entry[0]->entry);
    }
    else if (IS_AHAB_2(g_target))
    {
        cert_hdr_bytes = sizeof(ahab_container_certificate_v2_t);

        is_hybrid = is_hybrid_certificate(filename);

        if (is_hybrid < 0)
        {
            error("Error reading certificate");
        }

        num_records = is_hybrid ? CNTN_MAX_NR_SRK_TABLE : 1;

        cert_nxp->entry_bytes = cert_hdr_bytes;

        for (i = 0; i < num_records; i++)
        {
            /* Build the SRK record to retrieve the public key data */
            srk_entry[i] = cert_to_srk_entry(g_target, filename, i, 0,
                                             get_digest_name(hash), false);
            if (!srk_entry[i])
            {
                error("Cannot build an SRK entry from the certificate");
            }

            /* Generate the SRK data */
            srk_data[i] = generate_srk_data(
                0,
                (uint8_t *) srk_entry[i]->entry +
                    sizeof(struct ahab_container_srk_s),
                ((struct ahab_container_srk_s *) srk_entry[i]->entry)->length -
                    sizeof(struct ahab_container_srk_s),
                &srk_data_size[i]);

            if (!srk_data[i])
            {
                free(srk_entry[i]->entry);
                error("Cannot generate SRK data");
            }

            free(srk_entry[i]->entry);
            /* Rebuild the final SRK record */
            srk_entry[i] = cert_to_srk_entry(g_target, filename, i, 0,
                                             get_digest_name(hash), true);
            if (!srk_entry[i])
            {
                error("Cannot build an SRK entry from the certificate");
            }

            /* Construct the cert */
            cert_nxp->entry_bytes +=
                srk_entry[i]->entry_bytes + srk_data_size[i];
        }
        cert_nxp->entry = malloc(cert_nxp->entry_bytes);
        if (NULL == cert_nxp->entry)
        {
            error("Cannot allocate buffer fro handling the \'Certificate\'");
        }

        memset(cert_nxp->entry, 0, cert_nxp->entry_bytes);

        /* Check overflow */
        if ((signature_size + cert_nxp->entry_bytes) < signature_size)
        {
            error("Wrong \'Certificate\' or \'Signature\' definitions");
        }

        /* Fill in the certificate header */
        ahab_v2_cert = (ahab_container_certificate_v2_t *) (cert_nxp->entry);
        ahab_v2_cert->version = CERTIFICATE_VERSION_2;
        ahab_v2_cert->length = cert_nxp->entry_bytes + signature_size;
        ahab_v2_cert->tag = CERTIFICATE_TAG;
        ahab_v2_cert->signature_offset = cert_nxp->entry_bytes;
        ahab_v2_cert->permissions =
            ((uint16_t)(permissions) << 8) | (uint8_t)~permissions;
        ahab_v2_cert->perm_data[0] = permissions_data.first;
        ahab_v2_cert->perm_data[1] = permissions_data.second;
        ahab_v2_cert->perm_data[2] = permissions_data.third;
        ahab_v2_cert->fuse_version = fuse_version;
        ahab_v2_cert->uuid[0] = uuid.first;
        ahab_v2_cert->uuid[1] = uuid.second;
        ahab_v2_cert->uuid[2] = uuid.third;
        ahab_v2_cert->uuid[3] = uuid.fourth;

        offset = cert_hdr_bytes;

        for (i = 0; i < num_records; i++)
        {
            /* Copy SRK record to certificate */
            memcpy((uint8_t *) cert_nxp->entry + offset, srk_entry[i]->entry,
                   srk_entry[i]->entry_bytes);
            offset += srk_entry[i]->entry_bytes;

            /* Copy SRK data to certificate */
            memcpy((uint8_t *) cert_nxp->entry + offset, srk_data[i],
                   srk_data_size[i]);
            offset += srk_data_size[i];

            free(srk_data[i]);
            free(srk_entry[i]->entry);
        }

    } /* IS_AHAB_2(g_target) */
}

/*--------------------------
  convert_byte_str_to_file
---------------------------*/
void convert_byte_str_to_file(byte_str_t *data, const char *filename)
{
    FILE *file = NULL;

    /* Open the destination file */
    if ((file = fopen(filename, "wb")) == NULL)
    {
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to create binary file %s",
                 filename);
        error(err_msg);
    }

    /* Write the data */
    if (data->entry_bytes != fwrite(data->entry, 1, data->entry_bytes, file))
    {
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to write to binary file %s",
                 filename);
        error(err_msg);
    }

    fclose (file);
}

/*--------------------------
  generate_signature
---------------------------*/
void generate_signature(byte_str_t *data,
                        const char *data_filename_prefix,
                        const char *key,
                        hash_alg_t hash,
                        byte_str_t *sig,
                        const char *sig_filename)
{
    uint32_t   sig_hdr_bytes  = offsetof(struct ahab_container_signature_s, data);
    X509       *skey          = NULL;
    EVP_PKEY   *pkey          = NULL;
    sig_fmt_t  sig_fmt        = SIG_FMT_UNDEF;
    size_t     sig_bytes      = 0;
    byte_str_t sig_data       = {NULL, 0};
    uint8_t    *sha1          = NULL;
    size_t     digest_size    = 0;
    char       *data_filename = NULL;
    time_t t = 0;
    struct tm *t_info = NULL;
    char       t_str[4+2+2+2+2+2]; /* YYYY + MM + DD + HH + MM + "-\0" */
    struct ahab_container_signature_s *ahab_sig = NULL;
    int pkey_id = -1;
    uint8_t *sigs[CNTN_MAX_NR_SRK_TABLE] = {NULL, NULL};
    size_t sigs_size[CNTN_MAX_NR_SRK_TABLE] = {0, 0};
    size_t sig_offset = 0;
    int i;
    size_t num_sigs = 0;

    /* Get signing key information */
    skey = read_certificate(key);
    if (NULL == skey)
    {
        snprintf(err_msg, MAX_ERR_MSG_BYTES, "Unable to read %s", key);
        error(err_msg);
    }

    /* Get the public key  */
    pkey = X509_get_pubkey(skey);
    if (NULL == pkey)
    {
        X509_free(skey);
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to retrieve the public key from %s",
                 key);
        error(err_msg);
    }

    /* Build the signatures structure */
    sig->entry_bytes = get_signature_structure_size(key);
    sig->entry       = malloc(sig->entry_bytes);
    if (NULL == sig->entry)
    {
        error("Cannot allocate buffer for creating a signature");
    }
    memset(sig->entry, 0, sig->entry_bytes);

    if (get_signature_size(pkey, &sigs_size[0], &sigs_size[1]) != 1)
    {
        error("Error getting signature size");
    }

    sig_bytes = sigs_size[0] + sigs_size[1];

    for (int i = 0; i < CNTN_MAX_NR_SRK_TABLE; i++)
    {
        if (sigs_size[i] > 0)
        {
            ahab_sig =
                (struct ahab_container_signature_s *) ((uint8_t *) sig->entry +
                                                       sig_offset);
            ahab_sig->tag = SIGNATURE_TAG;
            ahab_sig->version = 0;
            ahab_sig->length = sigs_size[i] + sig_hdr_bytes;

            sigs[i] = (uint8_t *) sig->entry + sig_offset + sig_hdr_bytes;

            sig_offset += sig_hdr_bytes + sigs_size[i];

            num_sigs++;
        }
    }

    pkey_id = EVP_PKEY_id(pkey);

    if (pkey_id == EVP_PKEY_RSA)
    {
        sig_fmt = SIG_FMT_PKCS1;
    }
    else if (pkey_id == EVP_PKEY_RSA_PSS)
    {
        sig_fmt = SIG_FMT_RSA_PSS;
    }
    else if (pkey_id == EVP_PKEY_EC)
    {
        sig_fmt = SIG_FMT_ECDSA;
    }
#if CST_WITH_PQC
    else if (EVP_PKEY_is_a(pkey, "dilithium3") ||
             EVP_PKEY_is_a(pkey, "dilithium5"))
    {
        sig_fmt = SIG_FMT_DILITHIUM;
    }
    else if (EVP_PKEY_is_a(pkey, "mldsa65") || EVP_PKEY_is_a(pkey, "mldsa87"))
    {
        sig_fmt = SIG_FMT_MLDSA;
    }
    else if (IS_HYBRID(EVP_PKEY_get0_type_name(pkey)))
    {
        sig_fmt = SIG_FMT_HYBRID;
    }
#endif

    EVP_PKEY_free(pkey);
    X509_free(skey);

    /* Generate data filename */
    data_filename = malloc(strlen(data_filename_prefix)
                           + sizeof(t_str) - 1
                           + HASH_BYTES_SHA1 * 2
                           + sizeof(FILE_EXT_BIN));
    if (NULL == data_filename)
    {
        error("Unable to allocate memory for data filename");
    }
    strcpy(data_filename, data_filename_prefix);

    /* Add time information to filename */
    time(&t);
    t_info = localtime(&t);
    if (t_info == NULL)
    {
        error("Failed to convert time to local time");
    }

    if (strftime(t_str, sizeof(t_str), "%Y%m%d%H%M-", t_info) == 0)
    {
        error("Failed to format time string");
    }

    if (strcat(data_filename, t_str) == NULL)
    {
        error("Failed to concatenate time string to filename");
    }

    /* Add sha1 information to filename */
    sha1 = generate_hash(data->entry,
                         data->entry_bytes,
                         get_digest_name(SHA_1),
                         &digest_size);
    if (NULL == sha1)
    {
        error("Fail to generate the sha1 digest to be used as filename");
    }

    while (digest_size)
    {
        char tmp[4];
        snprintf(tmp, 4, "%02x", sha1[HASH_BYTES_SHA1 - digest_size--]);
        strcat(data_filename, tmp);
    }
    free(sha1);

    /* Add extension to filename */
    memcpy(data_filename
           + strlen(data_filename_prefix)
           + sizeof(t_str) - 1
           + HASH_BYTES_SHA1 * 2,
           FILE_EXT_BIN,
           sizeof(FILE_EXT_BIN));

    /* Create a tmp file for the signing process */
    convert_byte_str_to_file(data, data_filename);

    if (NULL == sig_filename)
    {
        /* Generate the signature */
        if (SUCCESS != gen_sig_data(data_filename, key, hash, sig_fmt, sigs[0],
                                    &sigs_size[0], sigs[1], &sigs_size[1],
                                    g_mode))
        {
            error("Unable to generate the signature");
        }
    }
    else
    {
        read_file(sig_filename, &sig_data, NULL);

        if (sig_bytes != sig_data.entry_bytes)
        {
            snprintf(err_msg, MAX_ERR_MSG_BYTES,
                     "The length of the signature file %s is not valid", sig_filename);
            error(err_msg);
        }

        if (CAL_SUCCESS != ver_sig_data(data_filename,
                                        key,
                                        hash,
                                        sig_fmt,
                                        sig_data.entry,
                                        sig_data.entry_bytes))
        {
            snprintf(err_msg, MAX_ERR_MSG_BYTES,
                     "The signature file %s is not valid", sig_filename);
            error(err_msg);
        }

        memcpy(sig->entry + sig_hdr_bytes, sig_data.entry, sig_data.entry_bytes);

        free(sig_data.entry);
    }

    if (MODE_HSM != g_mode)
    {
        if (0 != remove(data_filename))
        {
            snprintf(err_msg, MAX_ERR_MSG_BYTES, "Unable to delete %s", data_filename);
            error(err_msg);
        }
    }

    if (sig->entry_bytes != ((sig_hdr_bytes * num_sigs) + sig_bytes))
    {
        error("Unexpected signature length");
    }
}

/*--------------------------
  generate_output
---------------------------*/
void generate_output(FILE        *dst_tmp,
                     byte_str_t  *data,
                     offsets_t   *offsets,
                     const char  *dst)
{
    FILE     *file_dst = NULL;
    char     chr;
    uint32_t count, read_size;

    /* Create destination file */
    if ((file_dst = fopen(dst, "wb")) == NULL)
    {
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to create binary file %s",
                 dst);
        error(err_msg);
    }

    /* Fill destination file with source data until first offset */
    if (fseek(dst_tmp, 0, SEEK_SET) != 0)
    {
        error("Error seeking in destination file");
    }

    count = 0;
    while (offsets->first != count)
    {
        if (1 != fread(&chr, 1, 1, dst_tmp))
        {
            snprintf(err_msg, MAX_ERR_MSG_BYTES, "Unexpected read termination");
            error(err_msg);
        }

        if (1 != fwrite(&chr, 1, 1, file_dst))
        {
            snprintf(err_msg,
                    MAX_ERR_MSG_BYTES,
                    "Unable to write to binary file %s",
                    dst);
            error(err_msg);
        }

        count++;
    }

    /* Fill destination file with input data */
    if (data->entry_bytes != fwrite(data->entry, 1, data->entry_bytes, file_dst))
    {
        snprintf(err_msg,
                 MAX_ERR_MSG_BYTES,
                 "Unable to write to binary file %s",
                 dst);
        error(err_msg);
    }

    /* Fill destination file with remaining source data */
    if (fseek(dst_tmp, data->entry_bytes, SEEK_CUR) != 0)
    {
        error("Error seeking in destination file");
    }

    do
    {
        read_size = fread(&chr, 1, 1, dst_tmp);

        if (1 < read_size)
        {
            snprintf(err_msg, MAX_ERR_MSG_BYTES, "Unexpected read");
            error(err_msg);
        }

        if (read_size != fwrite(&chr, 1, read_size, file_dst))
        {
            snprintf(err_msg,
                    MAX_ERR_MSG_BYTES,
                    "Unable to write to binary file %s",
                    dst);
            error(err_msg);
        }
    } while (0 != read_size);

    fclose(dst_tmp);
    fclose(file_dst);

    printf("CSF Processed successfully and signed image available in %s\n", dst);
    exit(0);
}

/*--------------------------
  encrypt_images
---------------------------*/
void encrypt_images(ahab_data_t *ahab_data,
                    byte_str_t *cont_hdr,
                    const char *key,
                    uint8_t key_length,
                    FILE *destination)
{
    size_t iv_length = 16; /* The IV size for AES-CBC is 128 bits */
    uint8_t *iv = NULL;
    size_t digest_size = 0;
    struct ahab_container_image_s *image = NULL;
    uint8_t hash_type = 0;
    int32_t hash_size = 0;
    uint8_t i = 0;
    uint8_t *hash = NULL;
    FILE *plaintext = NULL;
    int32_t err_value = 0;
    byte_str_t enc_data = {0};
    uint32_t mask = 0;

    struct ahab_container_header_s *container_header
        = (struct ahab_container_header_s *)cont_hdr->entry;

    if (container_header->nrImages > AHAB_MAX_NR_IMAGES) {
        error("Invalid number of images");
        return;
    }

    mask = (container_header->nrImages == 32)
               ? 0xFFFFFFFF
               : (1U << container_header->nrImages) - 1;

    if (!(mask & ahab_data->image_indexes))
    {
        error("At least one image must be encrypted");
    }

    /** Encrypt the images **/

    image = get_ahab_image_array(container_header);

    for (i = 0; i < container_header->nrImages; i++)
    {

        if (0 == image->image_size || 0 == (ahab_data->image_indexes & (1U << i))) {
            image++;
            continue;
        }

        hash_type = (container_header->version == CONTAINER_HEADER_VERSION_2)
                        ? ahab_container_image_get_hash_2(image)
                        : ahab_container_image_get_hash_1(image);
        hash_size = ahab_get_hash_size_by_hash_type(hash_type);

        if (hash_size < 0) {
            error("Unsupported hash algorithm for image integrity");
        }

        /* Retrieve image data */

        offsets_t offsets = {
            .first  = ahab_data->offsets.first + image->image_offset,
            .second = offsets.first + image->image_size
        };

        byte_str_t image_data;

        read_file(ahab_data->source, &image_data, &offsets);

        if (NULL == image_data.entry) {
            error("Cannot allocate memory for the image index %d", i);
        }

        /* Create a temporary file with the data to be enrypted */
        plaintext = fopen(FILE_PLAIN_DATA, "wb");
        if (NULL == plaintext) {
            error("Cannot create temporary file for plaintext");
        }
        if (image_data.entry_bytes != fwrite(image_data.entry, 1, image_data.entry_bytes, plaintext)) {
            error("Fail to update the plaintext temporary file");
        }
        fclose(plaintext);

        /* Encrypt image data */

        iv = generate_hash(image_data.entry,
                        image_data.entry_bytes,
                        get_digest_name(SHA_256),
                        &digest_size);

        if ((iv == NULL) || (SHA256_DIGEST_LENGTH != digest_size))
        {
            error("Fail to generate IV of image index %d", i);
        }

        err_value = gen_auth_encrypted_data(
            FILE_PLAIN_DATA, FILE_ENCRYPTED_DATA, AES_CBC, NULL, 0,
            iv + SHA256_DIGEST_LENGTH - iv_length, iv_length, NULL, 0,
            key_length, g_cert_dek, key, g_reuse_dek);

        if (0 != remove(FILE_PLAIN_DATA)) {
            /* Coverity: checked return value, failure ignored intentionally */
        }

        if (CAL_SUCCESS != err_value) {
            error("Fail to generate encrypted data for image index %d", i);
        }

        /* Replace image data by encrypted data */

        read_file(FILE_ENCRYPTED_DATA, &enc_data, NULL);

        if (NULL == enc_data.entry) {
            error("Cannot allocate memory for the encrypted data of the image index %d", i);
        }

        if (0 != remove(FILE_ENCRYPTED_DATA)) {
            /* Coverity: checked return value, failure ignored intentionally */
        }

        if (image->image_size != enc_data.entry_bytes) {
            error("Unexpected size of encryted data of image index %d (%d)", i, enc_data.entry_bytes);
        }

        hash = generate_hash(enc_data.entry,
                            enc_data.entry_bytes,
                            get_digest_name(ahab_hash_2_cst_hash(hash_type)),
                            &digest_size);

        if ((hash == NULL) || (digest_size != (size_t) hash_size))
        {
            error("Fail to generate hash of encrypted data of image index %d", i);
        }

        memcpy(image->iv, iv, SHA256_DIGEST_LENGTH);
        memcpy(image->hash, hash, hash_size);
        memset(image->hash + hash_size, 0, SHA512_DIGEST_LENGTH - hash_size);
        image->flags |=
            (container_header->version == CONTAINER_HEADER_VERSION_2)
                ? IMAGE_FLAGS_ENCRYPTED_2
                : IMAGE_FLAGS_ENCRYPTED_1;

        if (fseek(destination, offsets.first, SEEK_SET) != 0)
        {
            error("Error seeking in destination file");
        }

        if (enc_data.entry_bytes != fwrite(enc_data.entry, 1, enc_data.entry_bytes, destination)) {
            error("Fail to replace image index %d with encrypted data", i);
        }

        free(enc_data.entry);
        free(image_data.entry);
        free(hash);
        free(iv);

        image++;
    }
}

/*===========================================================================
                               GLOBAL FUNCTIONS
=============================================================================*/

/*--------------------------
  convert_certificate
---------------------------*/
int32_t handle_ahab_signature(void)
{
    ahab_data_t *ahab_data = &g_ahab_data;
    byte_str_t  srk_table  = {NULL, 0};
    byte_str_t  cert       = {NULL, 0};
    byte_str_t  cert_sign  = {NULL, 0};
    byte_str_t  cont_hdr   = {NULL, 0};
    byte_str_t  sign_blk   = {NULL, 0};
    ahab_container_signature_block_t *ahab_sig_blk;
    byte_str_t  container  = {NULL, 0};
    byte_str_t  cont_unsig = {NULL, 0};
    byte_str_t  signature  = {NULL, 0};
    byte_str_t  blob       = {NULL, 0};
    hash_alg_t  hash       = INVALID_DIGEST;
    const char  *sign_key  = NULL;

    /* Get SRK table */
    if (NULL == ahab_data->srk_buff)
    {
        error("A SRK table must be installed");
    }

    srk_table.entry = ahab_data->srk_buff;
    srk_table.entry_bytes = ahab_data->srk_buff_size;

    /* Parse SRK table to extract hash algo information */
    hash = get_hash_alg(&srk_table);
    if (INVALID_DIGEST == hash)
    {
        error("Invalid hash algo defined in SRK table");
    }

    /* Handle a Certificate if present */
    if (NULL != ahab_data->certificate)
    {
        convert_to_nxp_format(
            ahab_data->certificate, &cert, ahab_data->permissions,
            ahab_data->permissions_data, ahab_data->uuid,
            ahab_data->fuse_version, hash,
            get_signature_structure_size(ahab_data->srk_entry));

        generate_signature(&cert,
                           "certificate-",
                           ahab_data->srk_entry,
                           hash,
                           &cert_sign,
                           ahab_data->cert_sign);

        sign_key = ahab_data->certificate;
    }
    else
    {
        sign_key = ahab_data->srk_entry;
    }

    /* Get Container header */
    read_file(ahab_data->source, &cont_hdr, &ahab_data->offsets);

    /* Check if signature block offset matches header information */
    if ((ahab_data->offsets.second - ahab_data->offsets.first) !=
        ((struct ahab_container_header_s *)(cont_hdr.entry))->signature_block_offset)
    {
        error("Offsets are not consistent with the input binary to be signed");
    }

    /* Copy source to temporary file */
    FILE *source = fopen(ahab_data->source, "rb");
    if (NULL == source ) {
        error("Cannot open %s", ahab_data->source);
    }
    FILE *destination = tmpfile();
    if (NULL == destination) {
        error("Cannot create temporary file");
    }

    char byte;
    while (0 != fread(&byte, 1, 1, source)) {
        if (1 != fwrite(&byte, 1, 1, destination)) {
            error("Fail to fill the temporary file");
        }
    }

    /* Handle a DEK if requested */
    if (NULL != ahab_data->dek)
    {
        /* Encrypt the images */
        encrypt_images(ahab_data, &cont_hdr, ahab_data->dek, ahab_data->dek_length, destination);

        blob.entry_bytes = AHAB_BLOB_HEADER + CAAM_BLOB_OVERHEAD_NORMAL + ahab_data->dek_length;
        blob.entry = malloc(blob.entry_bytes);
        if (NULL == blob.entry)
        {
            error("Cannot allocate memory for the Blob placeholder");
        }
        memset(blob.entry, 0xCA, blob.entry_bytes);
    }

    /* Create the Signature Block */
    sign_blk.entry_bytes = sizeof(ahab_container_signature_block_t) +
                           ALIGN(srk_table.entry_bytes, 8) +
                           ALIGN(get_signature_structure_size(sign_key), 8) +
                           ALIGN(cert.entry_bytes + cert_sign.entry_bytes, 8) +
                           blob.entry_bytes;
    sign_blk.entry = malloc(sign_blk.entry_bytes);
    if (NULL == sign_blk.entry)
    {
        error("Cannot allocate memory for the Signature block");
    }
    memset(sign_blk.entry, 0, sign_blk.entry_bytes);

    /* Update the Signature block */
    ahab_sig_blk = (ahab_container_signature_block_t *)(sign_blk.entry);
    ahab_sig_blk->tag                = SIGNATURE_BLOCK_TAG;
    ahab_sig_blk->version = IS_AHAB_2(g_target) ? SIGNATURE_BLOCK_VERSION_2
                                                : SIGNATURE_BLOCK_VERSION_1;
    ahab_sig_blk->length             = sign_blk.entry_bytes;
    ahab_sig_blk->srk_table_or_array_offset = sizeof(ahab_container_signature_block_t);
    ahab_sig_blk->signature_offset   = ahab_sig_blk->srk_table_or_array_offset
                                     + ALIGN(srk_table.entry_bytes, 8);
    ahab_sig_blk->certificate_offset =
        ahab_sig_blk->signature_offset +
        ALIGN(get_signature_structure_size(sign_key), 8);
    ahab_sig_blk->blob_offset        = ahab_sig_blk->certificate_offset
                                     + ALIGN(cert.entry_bytes + cert_sign.entry_bytes, 8);
    ahab_sig_blk->key_identifier     = ahab_data->key_identifier;

    /* Copy SRK table or SRK table array data */
    memcpy(sign_blk.entry + ahab_sig_blk->srk_table_or_array_offset,
           srk_table.entry,
           srk_table.entry_bytes);

    free(srk_table.entry);

    /* Copy Certificate data */
    if (NULL == cert.entry)
    {
        ahab_sig_blk->certificate_offset = 0;
    }
    else
    {
        memcpy(sign_blk.entry + ahab_sig_blk->certificate_offset,
               cert.entry,
               cert.entry_bytes);
        memcpy(sign_blk.entry
               + ahab_sig_blk->certificate_offset
               + cert.entry_bytes,
               cert_sign.entry,
               cert_sign.entry_bytes);

        free(cert.entry);
        free(cert_sign.entry);
    }

    /* Copy Blob data */
    if (NULL == blob.entry)
    {
        ahab_sig_blk->blob_offset = 0;
        ahab_sig_blk->key_identifier = 0;
    }
    else
    {
        memcpy(sign_blk.entry + ahab_sig_blk->blob_offset,
               blob.entry,
               blob.entry_bytes);

        free(blob.entry);

        printf("The DEK BLOB must be inserted at offset 0x%zx (its expected size is %zd bytes)\n",
            ahab_data->offsets.first
            + cont_hdr.entry_bytes
            + ahab_sig_blk->blob_offset,
            blob.entry_bytes);
    }

    /* Update Container header length */
    container.entry_bytes = cont_hdr.entry_bytes + sign_blk.entry_bytes;
    ((struct ahab_container_header_s *)(cont_hdr.entry))->length =
        container.entry_bytes;

    /* Update Container header flags */
    ((struct ahab_container_header_s *) (cont_hdr.entry))->flags =
        (((struct ahab_container_header_s *) (cont_hdr.entry))->flags &
         0xFFFF0000) |
        ahab_data->srk_set << HEADER_FLAGS_SRK_SET_SHIFT |
        ahab_data->srk_index << HEADER_FLAGS_SRK_SHIFT |
        ahab_data->revocations << HEADER_FLAGS_REVOKING_MASK_SHIFT |
        ahab_data->check_all_signatures
            << HEADER_FLAGS_CHECK_ALL_SIGNATURES_MASK_SHIFT |
        ahab_data->fast_boot << HEADER_FLAGS_FAST_BOOT_MASK_SHIFT;

    /* Build the signed Container */
    container.entry = malloc(container.entry_bytes);
    if (NULL == container.entry)
    {
        error("Cannot allocate memory for the Container");
    }
    memset(container.entry, 0, container.entry_bytes);

    memcpy(container.entry,
           cont_hdr.entry,
           cont_hdr.entry_bytes);

    memcpy(container.entry + cont_hdr.entry_bytes,
           sign_blk.entry,
           sign_blk.entry_bytes);

    /* Generate Container signature */
    cont_unsig.entry       = container.entry;
    cont_unsig.entry_bytes = cont_hdr.entry_bytes
                           + ahab_sig_blk->signature_offset;

    free(sign_blk.entry);

    generate_signature(&cont_unsig,
                       "container-",
                       sign_key,
                       hash,
                       &signature,
                       ahab_data->signature);

    memcpy(container.entry + cont_unsig.entry_bytes,
           signature.entry,
           signature.entry_bytes);

    free(signature.entry);

    /* Generate the output file */
    generate_output(destination,
                    &container,
                    &ahab_data->offsets,
                    ahab_data->destination);

    free(container.entry);

    return SUCCESS;
}
