/**
 * \file asn1build_rsa.c
 * \brief RSA-specific ASN.1 certificate builder.
 * \author Dmytro Podgornyi (d.podgornyi@samsung.com)
 * \version 0.1
 * \date Created May 28, 2013
 * \par In Samsung Ukraine R&D Center (SURC) under a contract between
 * \par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * \par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * \par Copyright: (c) Samsung Electronics Co, Ltd 2012. All rights reserved.
 **/

#include <stdint.h>
#include "CommLayerData.h"
#include "asn1build.h"
#include "asn1build_rsa.h"
#include "log.h"
#include "secMemoryManager.h"
#include "objects/obj_mac.h"

static unsigned long rsaEncryptionOID[] = {OBJ_rsaEncryption};//{1, 2, 840, 113549, 1, 1, 1};

static asn1_object_t rsaEncryption =
    {ARRAY_SIZE(rsaEncryptionOID), rsaEncryptionOID};

static asn1_gen_t AlgorithmIdentifierRSA[] =
{
    {ASN1_TYPE_OBJECT, &rsaEncryption},
    {ASN1_TYPE_NULL, NULL},
    {ASN1_TYPE_END, NULL},
};

int32_t asn1_build_keypair_rsa(uint8_t *out, uint32_t *out_len, crypto_t key)
{
    asn1_gen_t seq[10] = {ASN1GEN_INIT, ASN1GEN_INIT, ASN1GEN_INIT,
                          ASN1GEN_INIT, ASN1GEN_INIT, ASN1GEN_INIT,
                          ASN1GEN_INIT, ASN1GEN_INIT, ASN1GEN_INIT,
                          ASN1GEN_INIT};
    int i = 0;
    int32_t res = X509_INTERNAL_ERROR;
    asn1_string_t rsa_attrs[8];

    /* version */
    seq[0].type = ASN1_TYPE_LONG;
    seq[0].value = (void*)0;

    for (i = 0; i < ARRAY_SIZE(rsa_attrs); i++) {
        seq[i + 1].type = ASN1_TYPE_BIGNUM_RAW;
        seq[i + 1].value = &rsa_attrs[i];
        rsa_attrs[i].unused = 0;
        rsa_attrs[i].data = NULL;
    }
    if (CRYPTO_RSA_get_modulus           (key, (uint8_t**) &rsa_attrs[0].data, &rsa_attrs[0].size) != NOT_ERROR || 
        CRYPTO_RSA_get_public_exponent   (key, (uint8_t**) &rsa_attrs[1].data, &rsa_attrs[1].size) != NOT_ERROR ||
        CRYPTO_RSA_get_private_exponent  (key, (uint8_t**) &rsa_attrs[2].data, &rsa_attrs[2].size) != NOT_ERROR ||
        CRYPTO_RSA_get_prime1            (key, (uint8_t**) &rsa_attrs[3].data, &rsa_attrs[3].size) != NOT_ERROR ||
        CRYPTO_RSA_get_prime2            (key, (uint8_t**) &rsa_attrs[4].data, &rsa_attrs[4].size) != NOT_ERROR ||
        CRYPTO_RSA_get_exponent1         (key, (uint8_t**) &rsa_attrs[5].data, &rsa_attrs[5].size) != NOT_ERROR ||
        CRYPTO_RSA_get_exponent2         (key, (uint8_t**) &rsa_attrs[6].data, &rsa_attrs[6].size) != NOT_ERROR ||
        CRYPTO_RSA_get_coefficient       (key, (uint8_t**) &rsa_attrs[7].data, &rsa_attrs[7].size) != NOT_ERROR )
    {
        goto cleanup;
    }

    seq[9].type = ASN1_TYPE_END;
    seq[9].value = NULL;

    res = asn1_validate(seq);
    if (res != NOT_ERROR) {
        LOGE("asn1_build_pri_rsa: wrong ASN.1 format");
        goto cleanup;
    }

    res = asn1_gen_sequence(out, out_len, seq);
    if (res != NOT_ERROR) {
        goto cleanup;
    }

    res = NOT_ERROR;
cleanup:
    for (i = 0; i < ARRAY_SIZE(rsa_attrs); i++)
        secMemoryManagerFree(rsa_attrs[i].data);
    return res;
}

int32_t asn1_build_pub_rsa(const crypto_t key, uint8_t *out, uint32_t *outLen)
{
    asn1_gen_t seq[3] = {ASN1GEN_INIT, ASN1GEN_INIT, ASN1GEN_INIT};
    asn1_string_t rsa_attrs[2];
    asn1_gen_t pub_key[3] = {ASN1GEN_INIT, ASN1GEN_INIT, ASN1GEN_INIT};
    asn1_gen_t pub_key_seq[2] = {ASN1GEN_INIT, ASN1GEN_INIT};
    asn1_string_t pk_str = {0};
    uint32_t len = 0;
    uint8_t *pk_buf = NULL;
    uint32_t i = 0;
    int32_t result = X509_INTERNAL_ERROR;

    for (i = 0; i < ARRAY_SIZE(rsa_attrs); i++) {
        pub_key[i].type = ASN1_TYPE_BIGNUM_RAW;
        pub_key[i].value = &rsa_attrs[i];
        rsa_attrs[i].unused = 0;
        rsa_attrs[i].data = NULL;
    }

    if (CRYPTO_RSA_get_modulus           (key, (uint8_t**) &rsa_attrs[0].data, &rsa_attrs[0].size) != NOT_ERROR || 
        CRYPTO_RSA_get_public_exponent   (key, (uint8_t**) &rsa_attrs[1].data, &rsa_attrs[1].size) != NOT_ERROR)
    {
        result = ASN1_GEN_ERROR;
        goto err;
    }

    pub_key[2].type = ASN1_TYPE_END;
    pub_key[2].value = NULL;
    pub_key_seq[0].type = ASN1_TYPE_SEQUENCE;
    pub_key_seq[0].value = (void*)pub_key;
    pub_key_seq[1].type = ASN1_TYPE_END;
    pub_key_seq[1].value = NULL;
    len = asn1_get_field_bytes(pub_key_seq);
    pk_buf = secMemoryManagerMalloc(len);
    if (!pk_buf) {
        result = SEC_ALLOC_ERROR;
        goto err;
    }

    result = asn1_gen_field(pk_buf, len, pub_key_seq);
    if (result != NOT_ERROR) {
        goto err;
    }

    pk_str.size = len;
    pk_str.data = (char*)pk_buf;
    pk_str.unused = 0;

    seq[0].type = ASN1_TYPE_SEQUENCE;
    seq[0].value = (void*)AlgorithmIdentifierRSA;
    seq[1].type = ASN1_TYPE_BITSTRING;
    seq[1].value = (void*)&pk_str;
    seq[2].type = ASN1_TYPE_END;
    seq[2].value = NULL;

    len = asn1_get_field_bytes(seq);
    if (len > *outLen) {
        LOGE("asn1_build_pub_rsa_struct: buffer too small");
        result = BUFFER_OVERFLOW;
        goto err;
    }

    result = asn1_gen_sequence(out, outLen, seq);
err:
    for (i = 0; i < ARRAY_SIZE(rsa_attrs); i++)
        secMemoryManagerFree(rsa_attrs[i].data);
    if (pk_buf)
        secMemoryManagerFree(pk_buf);
    return result;
}
