/*
 * =====================================================================================
 *
 *       Filename:  certGenerator.c
 *
 *    Description:  X.509 service certificate generator.
 *
 *        Version:  1.0
 *        Created:  12/05/2017 10:56:00 AM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

#include <stdbool.h>
#include <stdint.h>
#include "commonConfig.h"
#include "certGenerator.h"
#include "certParser.h"
#include "cryptoPlatform.h"
#include "TLV.h"
#include "keyManager.h"
#include "log.h"
#include "secMemoryManager.h"
#include "teeCryptoApi.h"
#include "asn1build.h"
#include "objects/obj_mac.h"
#include "x509v3.h"
#include "sha/sha.h"

/* Name that should be removed from DRK's UID while constructing SK's UID */
#define DRK_V1_PREFIX          "PHN"
#define DRK_V2_PREFIX          "DRK_V2"
#define ROOT_SERVICE_NAME      "ROOT"

//#defined USE_SUBJECT_KEY_IDENTIFIER   // Deactivate compatability with Samsung pay server.

static unsigned long sha1WithRSAEncryptionOID[] = {OBJ_sha1WithRSAEncryption}; // {1, 2, 840, 113549, 1, 1, 5};
static unsigned long sha256WithRSAEncryptionOID[] = {OBJ_sha256WithRSAEncryption}; // {1, 2, 840, 113549, 1, 1, 11};
static unsigned long ecdsaWithSHA256EncryptionOID[] = {OBJ_ecdsa_with_SHA256};

static unsigned long keyUsageOID[] = {2, 5, 29, 15};
static unsigned long extKeyUsageOID[] = {2, 5, 29, 37};
static unsigned long authorityKeyIdentifierOID[] = {2, 5, 29, 35};
#if (defined USE_SUBJECT_KEY_IDENTIFIER)
static unsigned long subjectKeyIdentifierOID[] = {2, 5, 29, 14}; //{OBJ_subject_key_identifier};
#endif  // End of USE_SUBJECT_KEY_IDENTIFIER
static unsigned long basicConstraintsOID[] = {OBJ_basic_constraints};
static unsigned long subjectAltNameOID[] = {OBJ_subject_alt_name};

static uint8_t keyUsageStringRaw[2] = {0};

static asn1_string_t keyUsageString = {0, NULL, 0};
static asn1_string_t signature = {0, NULL, 0};

static asn1_object_t sha256WithRSAEncryption =
    {ARRAY_SIZE(sha256WithRSAEncryptionOID), sha256WithRSAEncryptionOID};
static asn1_object_t ecdsaWithSHA256Encryption =
    {ARRAY_SIZE(ecdsaWithSHA256EncryptionOID), ecdsaWithSHA256EncryptionOID};

static asn1_object_t keyUsage =
    {ARRAY_SIZE(keyUsageOID), keyUsageOID};
static asn1_object_t extKeyUsage =
    {ARRAY_SIZE(extKeyUsageOID), extKeyUsageOID};
static asn1_object_t authorityKeyIdentifier =
    {ARRAY_SIZE(authorityKeyIdentifierOID), authorityKeyIdentifierOID};
#if (defined USE_SUBJECT_KEY_IDENTIFIER)
static asn1_object_t subjectKeyIdentifier =
    {ARRAY_SIZE(subjectKeyIdentifierOID), subjectKeyIdentifierOID};
#endif  // End of USE_SUBJECT_KEY_IDENTIFIER
static asn1_object_t basicContraints =
    {ARRAY_SIZE(basicConstraintsOID), basicConstraintsOID};
static asn1_object_t subjectAltName =
    {ARRAY_SIZE(subjectAltNameOID), subjectAltNameOID};

static asn1_gen_t Version[] =
{
#define VERSION_IDX 0
    {ASN1_TYPE_LONG, NULL},
    {ASN1_TYPE_END, NULL},
};

/* algorithm used for signature */
static asn1_gen_t SignatureIdentifier[] =
{
    {ASN1_TYPE_OBJECT, &sha256WithRSAEncryption},
    {ASN1_TYPE_NULL, NULL},
    {ASN1_TYPE_END, NULL},
};

static asn1_gen_t keyUsageBitString =
    {ASN1_TYPE_BITSTRING, &keyUsageString};

static asn1_gen_t Validity[3] =
{
    ASN1GEN_INIT,
    ASN1GEN_INIT,
    ASN1GEN_INIT,
};

static asn1_gen_t authorityKeyIdentifier80[] =
{
    {ASN1_TYPE_RAW, NULL},
    {ASN1_TYPE_END, NULL},
};

static asn1_gen_t authorityKeyIdentifierInc[] =
{
    {ASN1_TYPE_IMPL_0, authorityKeyIdentifier80},
    {ASN1_TYPE_END, NULL},
};

static asn1_gen_t authorityKeyIdentifierExt =
    {ASN1_TYPE_SEQUENCE, authorityKeyIdentifierInc};

#if (defined USE_SUBJECT_KEY_IDENTIFIER)
static asn1_string_t subjectKeyIdentifierInc;

static asn1_gen_t subjectKeyIdentifierExt =
    {ASN1_TYPE_OCTETSTRING, &subjectKeyIdentifierInc};
#endif  // End of USE_SUBJECT_KEY_IDENTIFIER

static asn1_gen_t basicConstraintsInc[] =
{
    {ASN1_TYPE_BOOLEAN, NULL},
    {ASN1_TYPE_END, NULL},
};

static asn1_gen_t basicConstraintsExt =
    {ASN1_TYPE_SEQUENCE, basicConstraintsInc};

static asn1_gen_t Extensions[6] =
{
    ASN1GEN_INIT,
    ASN1GEN_INIT,
    ASN1GEN_INIT,
    ASN1GEN_INIT,
    ASN1GEN_INIT,
    ASN1GEN_INIT,
};

static asn1_gen_t ExtensionsA3[] =
{
    {ASN1_TYPE_SEQUENCE, Extensions},
    {ASN1_TYPE_END, NULL},
};

/* NULL fields must be set in genCertASN1() */
static asn1_gen_t TBSCertificate[] =
{
#define TBS_VERSION_IDX 0
    {ASN1_TYPE_EXPL_0, (void*)Version},
#define TBS_SERIAL_IDX 1
    {ASN1_TYPE_LONG, NULL},
#define TBS_ALGO_ID_IDX 2
    {ASN1_TYPE_SEQUENCE, (void*)SignatureIdentifier},
#define TBS_ISSUER_IDX 3
    {ASN1_TYPE_SEQUENCE, NULL},
#define TBS_VALIDITY_IDX 4
    {ASN1_TYPE_SEQUENCE, (void*)Validity},
#define TBS_SUBJECT_IDX 5
    {ASN1_TYPE_SEQUENCE, NULL},
#define TBS_PUBLIC_KEY_IDX 6
    {ASN1_TYPE_SEQUENCE, NULL},
#define TBS_EXTENSIONS_IDX 7
    {ASN1_TYPE_EXPL_3, (void*)ExtensionsA3},
    {ASN1_TYPE_END, NULL},
};

/* signature must be appended in genCertASN1() */
static asn1_gen_t Certificate[] =
{
#define CERT_TBS_IDX 0
    {ASN1_TYPE_SEQUENCE, (void*)TBSCertificate},
#define CERT_ALGO_ID_IDX 1
    {ASN1_TYPE_SEQUENCE, (void*)SignatureIdentifier},
#define CERT_SIGN_IDX 2
    {ASN1_TYPE_BITSTRING, (void*)&signature},
    {ASN1_TYPE_END, NULL},
};

static struct {
    asn1_string_t exponent;
    asn1_string_t dnqualifier;
    asn1_string_t keyusage;
    asn1_string_t extKeyusage;
    asn1_string_t signAlgo;
    asn1_string_t subjectAltName;
} substituteAttrs;

static char uid[MAX_UID_SIZE] = {0};

static asn1_string_t* getAttrSubstPtr(TlvTag_t attr)
{
    switch (attr)
    {
        case TLV_EXPONENT:
            return &substituteAttrs.exponent;

        case TLV_DN_QUALIFIER:
            return &substituteAttrs.dnqualifier;

        case TLV_KEYUSAGE:
            return &substituteAttrs.keyusage;

        case TLV_EXT_KEYUSAGE:
            return &substituteAttrs.extKeyusage;

        case TLV_HASH_ALGO:
            return &substituteAttrs.signAlgo;

        case TLV_SUBJECT_ALTER_NAME:
            return &substituteAttrs.subjectAltName;

        default:
            LOGE("%s : unsupported attrs type - %d.", __func__, attr);
    }

    return NULL;
}

int32_t setAttrSubst(TlvTag_t attr, void *raw, int size)
{
    asn1_string_t *ptr = getAttrSubstPtr(attr);

    if(ptr == NULL)
    {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    ptr->data = (char *)raw;
    ptr->size = size;

    return NOT_ERROR;
}

int32_t getAttrSubst(TlvTag_t attr, asn1_string_t **raw, uint32_t *size)
{
    asn1_string_t *ptr = getAttrSubstPtr(attr);

    if(ptr == NULL || raw == NULL || size == NULL)
    {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    if(ptr->data == NULL)
    {
        LOGE("%s : Attr's data is invalid.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    *raw = (asn1_string_t*)ptr->data;
    *size = ptr->size;

    return NOT_ERROR;
}

void initAttrSubst(void)
{
    memset(&substituteAttrs, 0, sizeof(substituteAttrs));
}

uint32_t getTlvExponent()
{
    unsigned long result = 0;
    asn1_string_t* exponent = NULL;
    uint32_t exponentSize = 0;
    struct asn1_hdr hdr = {0};
    int32_t i = 0;

    if(getAttrSubst(TLV_EXPONENT, &exponent, &exponentSize) != NOT_ERROR)
        return RSA_DEFAULT_EXPONENT;

    if(asn1_get_next((const u8*)exponent, exponentSize, &hdr) ||
            hdr.tag != ASN1_TAG_INTEGER || hdr.length > 4)
    {
        LOGI("%s : exponent attribute corrupted.", __func__);
        return RSA_DEFAULT_EXPONENT;
    }

    for (i = 0; i < hdr.length; i++)
        result = (result << 8) + hdr.payload[i];

    if (result != 3 && result != 65537) {
        result = RSA_DEFAULT_EXPONENT;
    }
    return result;
}

int32_t parseSubstituteAttrs(const uint8_t* attr, uint32_t attrLen)
{
    int32_t ret = 0, i = 0;
    uint8_t* arg;
    uint16_t argLen;
    TlvTag_t tlvList[] = {
        TLV_EXPONENT,
        TLV_HASH_ALGO,
        TLV_KEYUSAGE,
        TLV_DN_QUALIFIER,
        TLV_SUBJECT_ALTER_NAME
    };
    int32_t tlvListSize= ARRAY_SIZE(tlvList);

    initAttrSubst();

    for(i = 0; i < tlvListSize; i++)
    {
        if(tlvGet(attr, attrLen, tlvList[i], &argLen, &arg) != NOT_ERROR)
            continue;

        if((ret = setAttrSubst(tlvList[i], (void*)arg, (int)argLen)) != NOT_ERROR)
        {
            LOGE("Failed to setAttrSubst with error %d.", ret);
            return ret;
        }
    }

    return NOT_ERROR;
}

static int32_t generateUid(const char *issuerUid, const uint32_t issuerUidLen,
        const ServiceKeyInfo_t *serviceKeyInfo, char *uid, uint32_t uidLen)
{
    uint32_t lcFixedLen = 0;
    if(issuerUid == NULL || issuerUidLen == 0 || uid == NULL)
    {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    LOGD("Generating UID: issuerUidLen = %d, buffer = %d", issuerUidLen, uidLen);

    memset(uid, 0, uidLen);

    // DRK v1 format - PHN-D:20130614:00:20:00000020:ROOT
    if(!strncmp(issuerUid, DRK_V1_PREFIX, strlen(DRK_V1_PREFIX))
        && !strncmp(issuerUid + issuerUidLen - strlen(ROOT_SERVICE_NAME), ROOT_SERVICE_NAME, strlen(ROOT_SERVICE_NAME)))
    {
        lcFixedLen = MAX_SERVICE_NAME + MAX_MODEL_SIZE + MAX_SERIALNO_SIZE + 2;
        if((uidLen > lcFixedLen) &&
           (issuerUidLen > strlen(ROOT_SERVICE_NAME)) &&
           (uidLen - lcFixedLen > issuerUidLen - strlen(ROOT_SERVICE_NAME))) {
            strncpy(uid, issuerUid, issuerUidLen - strlen(ROOT_SERVICE_NAME));
            strncat(uid, serviceKeyInfo->serviceName, (strlen(serviceKeyInfo->serviceName) < MAX_SERVICE_NAME) ?
                    strlen(serviceKeyInfo->serviceName) : MAX_SERVICE_NAME);
            strncat(uid, ":", sizeof(char));
            strncat(uid, serviceKeyInfo->model, (strlen(serviceKeyInfo->model) < MAX_MODEL_SIZE) ?
                    strlen(serviceKeyInfo->model) : MAX_MODEL_SIZE);
            strncat(uid, ":", sizeof(char));
            strncat(uid, serviceKeyInfo->serialNo, (strlen(serviceKeyInfo->serialNo) < MAX_SERIALNO_SIZE) ?
                    strlen(serviceKeyInfo->serialNo) : MAX_SERIALNO_SIZE);
        } else {
            LOGE("Uid buffer is too small.");
            return ERR_TA_BUFFER_OVERFLOW;
        }
    }

    // DRK v2 format - DRK_V2:20170518155848:SWR:y_GO_zgwZYnsPEfjdTEgzOTDV-XblxconW1j1Bidg7o=:l29JiaCy9ZeJQvvj_NLBv2iWKTgirEr3dXk9Il6PneQ
    else if(!strncmp(issuerUid, DRK_V2_PREFIX, strlen(DRK_V2_PREFIX)))
    {
        lcFixedLen = MAX_SERVICE_NAME + 1;
        if((uidLen > lcFixedLen) && (uidLen - lcFixedLen > issuerUidLen))
        {
            strncpy(uid, issuerUid, issuerUidLen);
            strncat(uid, ":", sizeof(char));
            strncat(uid, serviceKeyInfo->serviceName, (strlen(serviceKeyInfo->serviceName) < MAX_SERVICE_NAME) ?
                strlen(serviceKeyInfo->serviceName) : MAX_SERVICE_NAME);
        } else {
            LOGE("Uid buffer is too small.");
            return ERR_TA_BUFFER_OVERFLOW;
        }
    }
    else
    {
        LOGE("Issuer UID(from device root certificate) is invalid.");
        return ERR_TA_INVALID_ARGUMENT;
    }

    LOGD("UID is generated successful : %s", uid);

    return NOT_ERROR;
}

static int32_t asn1_long_to_bit_string(unsigned long val, asn1_string_t* str, uint8_t* str_raw, uint32_t str_raw_len)
{
    int32_t i = 0, used_bits = -1, used_bytes = 0;
    uint8_t buf[sizeof(unsigned long)] = {0};
    uint8_t* ptr = buf;

    if(str == NULL || str_raw == NULL)
    {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    // For every single bit in val...
    for (i = 1; i <= sizeof(unsigned long) * 8; i++)
    {
        if (val & 1)
        {
            // Find the number of used bits in bit string
            used_bits = i;
            *ptr |= 1;
        }

        // Step to the next output byte or shift current one to the LEFT.
        if (i % 8 == 0)
            ptr++;
        else
            *ptr <<= 1;
        // Analyze next bit...
        val >>= 1;
    }

    // Find number of required bytes
    used_bytes = used_bits / 8 + ( used_bits % 8 ? 1 : 0 );

    if (used_bytes > str_raw_len)
        return ERR_TA_BUFFER_OVERFLOW;

    memcpy(str_raw, buf, used_bytes);
    str->size = used_bytes;
    str->data = (char*)str_raw;
    str->unused = used_bytes * 8 - used_bits;

    return NOT_ERROR;
}

static int32_t genCertStruct(struct x509_certificate* cert, const struct x509_certificate *issuer,
        const ServiceKeyInfo_t *serviceKeyInfo)
{
    uint16_t serialNo = 0;
    int32_t i = 0, mon = 0;
    int32_t ret = NOT_ERROR;

    if((ret = getRandBlock((uint8_t *)&serialNo, sizeof(serialNo))) != sizeof(serialNo))
    {
        LOGE("Failed to make serial no with error %d.", ret);
        return ret;
    }

    serialNo &= 0x7FFF;

    if((issuer->exts.extensions_present & X509_EXT_SUBJECT_KEY_IDENTIFIER) == 0)
    {
        LOGE("No subject key identifier.");
        return ERR_TA_INVALID_ARGUMENT;
    }

    cert->next = NULL;
    cert->version = X509_CERT_V3;
    cert->serial_number = serialNo;
    cert->signature = issuer->signature;
    cert->issuer = issuer->subject;
    cert->subject = issuer->subject;

    for(i = 0; i < cert->subject.num_attr; i++)
    {
        if (cert->subject.attr[i].type == X509_NAME_ATTR_UID)
            break;
    }

    if(i == cert->subject.num_attr)
    {
        if (i == X509_MAX_NAME_ATTRIBUTES)
        {
            LOGE("Subject number attributes reaches maximum.");
            return ERR_TA_INVALID_ARGUMENT;
        }

        cert->subject.attr[i].type = X509_NAME_ATTR_UID;
        cert->subject.num_attr++;
    }

    if((ret = generateUid(cert->subject.attr[i].value, cert->subject.attr[i].value_size,
                serviceKeyInfo, uid, sizeof(uid))) != NOT_ERROR)
    {
        LOGE("Failed to make Uid with error %d.", ret);
        return ret;
    }

    cert->subject.attr[i].value = uid;
    cert->subject.attr[i].value_size = strlen(uid);

    cert->not_before.year = issuer->not_before.year;
    cert->not_before.mon = issuer->not_before.mon;
    cert->not_before.mday = issuer->not_before.mday;
    cert->not_before.hour = issuer->not_before.hour;
    cert->not_before.min = issuer->not_before.min ;
    cert->not_before.sec = issuer->not_before.sec ;
    cert->not_before.start = NULL;
    cert->not_before.end = NULL;

    cert->not_after = cert->not_before;

    mon = cert->not_before.mon + CERT_VALIDATION_PERIOD;
    --mon;

    cert->not_after.year = cert->not_before.year + mon / 12;
    cert->not_after.mon = mon % 12 + 1;

    if(cert->not_after.mday > 28)
    {
        cert->not_after.mday = 28;
    }

    cert->signature_alg = issuer->signature_alg;

    cert->sign_value = NULL;
    cert->sign_value_len = 0;

    cert->exts = issuer->exts;
    cert->exts.authorityKeyIdentifier.length = issuer->exts.subjectKeyIdentifier.length;
    cert->exts.authorityKeyIdentifier.data = issuer->exts.subjectKeyIdentifier.data;

    cert->exts.extensions_present =
        X509_EXT_AUTHORITY_KEY_IDENTIFIER | X509_EXT_SUBJECT_KEY_IDENTIFIER |
        X509_EXT_KEY_USAGE                | X509_EXT_BASIC_CONSTRAINTS;

    cert->exts.key_usage =
        X509_KEY_USAGE_DIGITAL_SIGNATURE | X509_KEY_USAGE_NON_REPUDIATION |
        X509_KEY_USAGE_KEY_ENCIPHERMENT  | X509_KEY_USAGE_DATA_ENCIPHERMENT;

    cert->exts.ca = false;
    return NOT_ERROR;
}

static int32_t genCertASN1(uint8_t *out, uint32_t *out_len, const struct x509_certificate *issuer,
        struct x509_certificate *cert, const KEY *public_key, const KEY *ca)
{
    int32_t ret = NOT_ERROR;
    char notbefore[16] = {0}, notafter[16] = {0};
    int i = 0, algo = 0;
    asn1_string_t keyId = {0};
    uint8_t* signatureValue = NULL;
    uint32_t extIndex = 0, signatureLength = 0;
    struct x509_algorithm_identifier signature_alg;
    uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
    uint32_t digestLen = sizeof(digest);
    uint8_t pk_buf[MAX_KEY_LEN] = {0};
    asn1_string_t pk_rawstring = {0};

    pk_rawstring.size = sizeof(pk_buf);
    pk_rawstring.unused = 0;
    pk_rawstring.data = (char*)pk_buf;

    Version[VERSION_IDX].value = (void*)cert->version;
    TBSCertificate[TBS_SERIAL_IDX].value = (void*)cert->serial_number;

    if((ret = KEY_build_public(public_key, (uint8_t*)pk_rawstring.data, (uint32_t*)&pk_rawstring.size)) != NOT_ERROR)
    {
        LOGE("Failed to get public key with error %d.", ret);
        return ret;
    }

    TBSCertificate[TBS_PUBLIC_KEY_IDX].value = &pk_rawstring;
    TBSCertificate[TBS_PUBLIC_KEY_IDX].type = ASN1_TYPE_RAW;

    /* AlgorithmIdentifier */
    if(substituteAttrs.signAlgo.data != NULL)
    {
        // Validate signature algorithm
        const uint8_t* pos = (const uint8_t*)substituteAttrs.signAlgo.data;
        if(asn1_get_oid(pos, substituteAttrs.signAlgo.size, &signature_alg.oid, &pos))
        {
            LOGE("Invalid sign hash algorithm.");
            ret = ERR_TA_INVALID_TLV_ATTR;
            goto free_pkey;
        }
    }

    switch(getCertPublicKeyType(issuer))
    {
        case RSA_KEY:
            if(substituteAttrs.signAlgo.data != NULL &&
                    (!memcmp(sha256WithRSAEncryptionOID, signature_alg.oid.oid,
                             sizeof(sha256WithRSAEncryptionOID)) ||
                     !memcmp(sha1WithRSAEncryptionOID, signature_alg.oid.oid,
                         sizeof(sha1WithRSAEncryptionOID))))
            {
                SignatureIdentifier[0].value = (void*)&substituteAttrs.signAlgo;
                SignatureIdentifier[0].type = ASN1_TYPE_RAW;
            }
            else
            {
                SignatureIdentifier[0].value = &sha256WithRSAEncryption;
                SignatureIdentifier[0].type = ASN1_TYPE_OBJECT;
            }
            break;

        case ECC_KEY:
            SignatureIdentifier[0].value = &ecdsaWithSHA256Encryption;
            SignatureIdentifier[1].value = NULL;
            SignatureIdentifier[1].type = ASN1_TYPE_END;

            if(substituteAttrs.signAlgo.data != NULL &&
                    !memcmp(ecdsaWithSHA256EncryptionOID, signature_alg.oid.oid,
                        sizeof(ecdsaWithSHA256EncryptionOID)))
            {
                SignatureIdentifier[0].value = (void*)&substituteAttrs.signAlgo;
                SignatureIdentifier[0].type = ASN1_TYPE_RAW;
            }
            else
            {
                SignatureIdentifier[0].value = &ecdsaWithSHA256Encryption;
                SignatureIdentifier[0].type = ASN1_TYPE_OBJECT;
            }
            break;

        default:
            LOGE("Unknown public key type : %d.", getCertPublicKeyType(issuer));
            break;
    }

    /* Names (issuer, subject) */
    if((TBSCertificate[TBS_ISSUER_IDX].value = (void*)asn1_build_name(&cert->issuer)) == NULL)
    {
        LOGE("Failed to make ASN.1 data for issuer.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_pkey;
    }

    if(substituteAttrs.dnqualifier.data != NULL) {
        if(cert->subject.num_attr < X509_MAX_NAME_ATTRIBUTES)
        {
            cert->subject.attr[cert->subject.num_attr].type = X509_NAME_ATTR_DNQUAL;
            cert->subject.attr[cert->subject.num_attr].value = substituteAttrs.dnqualifier.data;
            cert->subject.attr[cert->subject.num_attr].value_size = substituteAttrs.dnqualifier.size;
            cert->subject.num_attr++;
        }
        else
        {
            LOGE("Subject attributes were over the maximum.");
        }
    }

    if((TBSCertificate[TBS_SUBJECT_IDX].value = (void*)asn1_build_name(&cert->subject)) == NULL)
    {
        LOGE("Failed to make ASN.1 data for subject.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_issuer;
    }

    /* Validity */
    if(ARRAY_SIZE(Validity) < 3)
    {
        LOGE("%s : Not enough space to set Validity.", __func__);
        ret = ERR_TA_BUFFER_OVERFLOW;
        goto free_subject;
    }

    if((ret = build_utc_time(notbefore, sizeof(notbefore), notafter, sizeof(notafter), cert)) != NOT_ERROR)
    {
        LOGE("Failed to make UTC time with error %d.", ret);
        goto free_subject;
    }

    Validity[0].type = ASN1_TYPE_UTCTIME;
    Validity[0].value = (void*)notbefore;
    Validity[1].type = ASN1_TYPE_UTCTIME;
    Validity[1].value = (void*)notafter;
    Validity[2].type = ASN1_TYPE_END;
    Validity[2].value = NULL;

    // Set extension fields.
    for (i = 0; i < ARRAY_SIZE(Extensions); i++)
        Extensions[i].type = ASN1_TYPE_END;

    Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;

    if(substituteAttrs.keyusage.data != NULL)
    {
        Extensions[extIndex].value = (void*)asn1_build_ext_raw(&keyUsage,
                (uint8_t*)substituteAttrs.keyusage.data, substituteAttrs.keyusage.size,
                ASN1_CRITICAL);
    }
    else
    {
        if((ret = asn1_long_to_bit_string(cert->exts.key_usage, &keyUsageString, keyUsageStringRaw, sizeof(keyUsageStringRaw))) != NOT_ERROR)
        {
            LOGE("Failed to make ASN.1 data with error %d.", ret);
            goto free_extentions;
        }

        Extensions[extIndex].value =
            (void*)asn1_build_ext(&keyUsage, &keyUsageBitString, ASN1_CRITICAL);
    }

    if(Extensions[extIndex].value == NULL)
    {
        LOGE("Missed setting extension field data.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_extentions;
    }

    extIndex++;

#if (defined USE_SUBJECT_KEY_IDENTIFIER)
    subjectKeyIdentifierInc.size = cert->exts.subjectKeyIdentifier.length;
    subjectKeyIdentifierInc.data = (char *)cert->exts.subjectKeyIdentifier.data;
    Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;
    Extensions[extIndex].value =
        (void*)asn1_build_ext(&subjectKeyIdentifier, &subjectKeyIdentifierExt, ASN1_NON_CRITICAL);

    if(Extensions[extIndex].value == NULL)
    {
        LOGE("Missed setting extension field data.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_extentions;
    }

    extIndex++;
#endif  // End of USE_SUBJECT_KEY_IDENTIFIER

    keyId.size = cert->exts.authorityKeyIdentifier.length;
    keyId.data = (char *)cert->exts.authorityKeyIdentifier.data;
    authorityKeyIdentifier80[0].value = &keyId;
    Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;
    Extensions[extIndex].value =
        (void*)asn1_build_ext(&authorityKeyIdentifier, &authorityKeyIdentifierExt, ASN1_NON_CRITICAL);

    if(Extensions[extIndex].value == NULL)
    {
        LOGE("Missed setting extension field data.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_extentions;
    }

    extIndex++;

    basicConstraintsInc[0].type = ASN1_TYPE_BOOLEAN;
    // replace "true" in exts.ca of int32 type as "0xff" to avoid issues on 64bit sWd build.
    basicConstraintsInc[0].value = cert->exts.ca ? (void*)0xff:(void*)0;
    Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;
    Extensions[extIndex].value =
        (void*)asn1_build_ext(&basicContraints, &basicConstraintsExt, ASN1_NON_CRITICAL);

    if(Extensions[extIndex].value == NULL)
    {
        LOGE("Missed setting extension field data.");
        ret = ERR_TA_GEN_ASN1_FAILED;
        goto free_extentions;
    }

    extIndex++;

    if(substituteAttrs.extKeyusage.data != NULL)
    {
        Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;
        Extensions[extIndex].value =
            (void*)asn1_build_ext_raw(&extKeyUsage, (uint8_t *)substituteAttrs.extKeyusage.data,
                    substituteAttrs.extKeyusage.size, ASN1_NON_CRITICAL);
        extIndex++;
    }

    if(substituteAttrs.subjectAltName.data != NULL)
    {
        Extensions[extIndex].type = ASN1_TYPE_SEQUENCE;
        Extensions[extIndex].value =
            (void*)asn1_build_ext_raw(&subjectAltName, (uint8_t *)substituteAttrs.subjectAltName.data,
                    substituteAttrs.subjectAltName.size, ASN1_NON_CRITICAL);
        extIndex++;
    }

    signatureLength = KEY_signature_size(ca);

    if((signatureValue = secMemoryManagerMalloc(signatureLength)) == NULL)
    {
        LOGE("Failed to allocate memory for signatureValue.");
        ret = ERR_TA_NOT_ENOUGH_MEMORY;
        goto free_extentions;
    }

    signature.size = signatureLength;
    signature.data = (char*)signatureValue;

    if((ret = asn1_validate(Certificate)) != NOT_ERROR)
    {
        LOGE("Certificate has wrong ASN.1 structure with error %d.", ret);
        goto free_signature;
    }

    if((ret = asn1_gen_sequence(out, out_len, Certificate)) != NOT_ERROR)
    {
        LOGE("Failed to generate sequence with error %d.", ret);
        goto free_signature;
    }

    memset(cert, 0, sizeof(*cert));

    if((ret = x509_certificate_parse(out, *out_len, cert)) != NOT_ERROR)
    {
        LOGE("Failed to parse x.509 certificate with error %d.", ret);
        ret += ERR_TA_X509_PARSE_BASE;
        goto free_signature;
    }

    if((ret = getCertHash(cert, &algo, digest, &digestLen)) != NOT_ERROR)
    {
        LOGE("Failed to make certificate hash with error %d.", ret);
        goto free_signature;
    }

    if((ret = KEY_sign((KEY*)ca, algo, digest, digestLen, signatureValue, &signatureLength)) != NOT_ERROR)
    {
        LOGE("Failed to sign for certificate with errot %d.", ret);
        goto free_signature;
    }

#if (defined RUN_FUNC_TESTS)
    if((ret = KEY_verify((KEY*)ca, algo, digest, digestLen, signatureValue, signatureLength)) != NOT_ERROR)
    {
        LOGE("FUNC TEST: Signature verification of signed certificate is failed with error %d.", ret);

        goto free_signature;
    }
#endif

    signature.size = signatureLength;
    signature.data = (char*)signatureValue;

    /* Generation */
    if((ret = asn1_validate(Certificate)) != NOT_ERROR)
    {
        LOGE("Failed to validate ASN.1 certificate with error %d.", ret);
    }

    if((ret = asn1_gen_sequence(out, out_len, Certificate)) != NOT_ERROR)
    {
        LOGE("Failed to generate sequence with error %d.", ret);
        goto free_signature;
    }

    ret = NOT_ERROR;

free_signature:
    if(signatureValue)
    {
        memset(signatureValue, 0, signatureLength);
        secMemoryManagerFree(signatureValue);
    }

free_extentions:
    for (i = 0; i < ARRAY_SIZE(Extensions) && Extensions[i].type != ASN1_TYPE_END; i++)
    {
        if(Extensions[i].type != ASN1_TYPE_RAW)
            asn1_free_ext((asn1_gen_t*)Extensions[i].value);
    }

free_subject:
    if (TBSCertificate[TBS_SUBJECT_IDX].type != ASN1_TYPE_RAW)
        asn1_free((asn1_gen_t*)TBSCertificate[TBS_SUBJECT_IDX].value);

free_issuer:
    asn1_free((asn1_gen_t*)TBSCertificate[TBS_ISSUER_IDX].value);

free_pkey:
    return ret;
}

int32_t generateCertificate(uint8_t *outData, uint32_t *outDataLen, const ServiceKeyInfo_t *serviceKeyInfo,
        const struct x509_certificate *issuer, KEY *service, KEY *ca)
{
    int32_t ret = NOT_ERROR;
    struct x509_certificate cert;

    if(outData == NULL || outDataLen == NULL || serviceKeyInfo == NULL || issuer == NULL || service == NULL || ca == NULL)
    {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    LOGD("Begin of certificate generation, available space: %d B", *outDataLen);

    memset(&cert, 0, sizeof(cert));

    // Generate x509_certificate structure without public key.
    if((ret = genCertStruct(&cert, issuer, serviceKeyInfo)) != NOT_ERROR)
    {
        LOGE("Failed to generate cert structure with error %d.", ret);
        return ret;
    }

    // Generate certificate in DER format.
    if((ret = genCertASN1(outData, outDataLen, issuer, &cert, service, ca)) != NOT_ERROR)
    {
        LOGE("Failed to make ASN.1 format with error %d.", ret);
        return ret;
    }

#if (defined DEBUG)
    LOGD("Verify X.509 certificate....");

    if((ret = x509_certificate_parse(outData, *outDataLen, &cert)) != NOT_ERROR)
    {
        LOGE("Generated certificate is invalid with error %d.", ret);
        return ret;
    }

    LOGD("Verfication of X.509 certificate is successful.");
#endif  // End of DEBUG;

    return ret;
}
