/*
 *  X.509 certificate and private key writing
 *
 *  Copyright (C) 2006-2007  Pascal Vizeli <pvizeli@yahoo.de>
 *  Modifications (C) 2009 Steven Barth <steven@midlink.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License, version 2.1 as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301  USA
 */
/*
 *  The ITU-T X.509 standard defines a certificat format for PKI.
 *
 *  http://www.ietf.org/rfc/rfc2459.txt
 *  http://www.ietf.org/rfc/rfc3279.txt
 *
 *  ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
 *
 *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
 *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
 *
 *  For CRS:
 *  http://www.faqs.org/rfcs/rfc2314.html
 */
#include "polarssl/config.h"
#include "polarssl/x509.h"
#include "polarssl/base64.h"
#include "polarssl/sha1.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>

#define and &&
#define or ||

#if defined _MSC_VER && !defined snprintf
#define snprintf _snprintf
#endif

static int x509write_realloc_node(x509_node *node, size_t larger);
static int x509write_file(x509_node *node, char *path, int format, const char* pem_prolog, const char* pem_epilog);

/*
 * evaluate how mani octet have this integer
 */
static int asn1_eval_octet(unsigned int digit)
{
    int i, byte;

    for (byte = 4, i = 24; i >= 0; i -= 8, --byte)
        if (((digit >> i) & 0xFF) != 0)
            return byte;

    return 0;
}

/*
 * write the asn.1 lenght form into p
 */
static int asn1_add_len(unsigned int size, x509_node *node)
{
    if (size > 127) {

        /* long size */
        int byte = asn1_eval_octet(size);
        int i = 0;

        *(node->p) = (0x80 | byte) & 0xFF;
        ++node->p;

        for (i = byte; i > 0; --i) {

            *(node->p) = (size >> ((i - 1) * 8)) & 0xFF;
            ++node->p;
        }

    } else {

        /* short size */
        *(node->p) = size & 0xFF;
        if (size != 0)
            ++node->p;
    }

    return 0;
}

/*
 * write a ans.1 object into p
 */
static int asn1_add_obj(unsigned char *value, unsigned int size, int tag,
        x509_node *node)
{
    int tl = 2;

    if (tag == ASN1_BIT_STRING)
        ++tl;

    if (size > 127)
        x509write_realloc_node(node, (size_t) size + tl +
                asn1_eval_octet(size));
    else
        x509write_realloc_node(node, (size_t) size + tl);

    if (node->data == NULL)
        return 1;

    /* tag */
    *(node->p) = tag & 0xFF;
    ++node->p;

    /* len */
    if (tag == ASN1_BIT_STRING) {
        asn1_add_len((unsigned int) size + 1, node);
        *(node->p) = 0x00;
        ++node->p;
    } else {
        asn1_add_len((unsigned int) size, node);
    }

    /* value */
    if (size > 0) {

        memcpy(node->p, value, (size_t) size);
        if ((node->p += size -1) != node->end)
            return POLARSSL_ERR_X509_POINT_ERROR;
    } else {
        /* make nothing -> NULL */
    }

    return 0;
}

/*
 * write a asn.1 conform integer object
 */
static int asn1_add_int(signed int value, x509_node *node)
{
    signed int i = 0, neg = 1;
    unsigned int byte, u_val = 0, tmp_val = 0;

    /* if negate? */
    if (value < 0) {
        neg = -1;
        u_val = ~value;
    } else {
        u_val = value;
    }

    byte = asn1_eval_octet(u_val);
    /* 0 isn't NULL */
    if (byte == 0)
        byte = 1;

    /* ASN.1 integer is signed! */
    if (byte < 4 and ((u_val >> ((byte -1) * 8)) & 0xFF) == 0x80)
        byte += 1;

    if (x509write_realloc_node(node, (size_t) byte + 2) != 0)
        return 1;

    /* tag */
    *(node->p) = ASN1_INTEGER;
    ++node->p;

    /* len */
    asn1_add_len(byte, node);

    /* value */
    for (i = byte; i > 0; --i) {

        tmp_val = (u_val >> ((i - 1) * 8)) & 0xFF;
        if (neg == 1)
            *(node->p) = tmp_val;
        else
            *(node->p) = ~tmp_val;

        if (i > 1)
          ++node->p;
    }

    if (node->p != node->end)
        return POLARSSL_ERR_X509_POINT_ERROR;

    return 0;
}

/*
 * write a asn.1 conform mpi object
 */
static int asn1_add_mpi(mpi *value, int tag, x509_node *node)
{
    size_t size = (mpi_msb(value) / 8) + 1;
    unsigned char *buf;
    int buf_len = (int) size, tl = 2;

    if (tag == ASN1_BIT_STRING)
        ++tl;

    if (size > 127)
        x509write_realloc_node(node, size + (size_t) tl +
            asn1_eval_octet((unsigned int)size));
    else
        x509write_realloc_node(node, size + (size_t) tl);

    if (node->data == NULL)
        return 1;

    buf = (unsigned char*) malloc(size);
    if (mpi_write_binary(value, buf, buf_len) != 0)
        return POLARSSL_ERR_MPI_BUFFER_TOO_SMALL;

    /* tag */
    *(node->p) = tag & 0xFF;
    ++node->p;

    /* len */
    if (tag == ASN1_BIT_STRING) {
        asn1_add_len((unsigned int) size + 1, node);
        *(node->p) = 0x00;
        ++node->p;
    } else {
        asn1_add_len((unsigned int) size, node);
    }

    /* value */
    memcpy(node->p, buf, size);
    free(buf);

    if ((node->p += (int) size -1) != node->end)
        return POLARSSL_ERR_X509_POINT_ERROR;

    return 0;
}

/*
 * write a node into asn.1 conform object
 */
static int asn1_append_tag(x509_node *node, int tag)
{
    int tl = 2;

    x509_node tmp;
    x509write_init_node(&tmp);

    if (tag == ASN1_BIT_STRING)
        ++tl;

    if (node->len > 127)
        x509write_realloc_node(&tmp, node->len + (size_t) tl +
            asn1_eval_octet((unsigned int)node->len));
    else
        x509write_realloc_node(&tmp, node->len + (size_t) tl);

    if (tmp.data == NULL) {
        x509write_free_node(&tmp);
        return 1;
    }

    /* tag */
    *(tmp.p) = tag & 0xFF;
    ++tmp.p;

    /* len */
    if (tag == ASN1_BIT_STRING) {
        asn1_add_len((unsigned int) node->len + 1, &tmp);
        *(tmp.p) = 0x00;
        ++tmp.p;
    } else {
        asn1_add_len((unsigned int) node->len, &tmp);
    }

    /* value */
    memcpy(tmp.p, node->data, node->len);

    /* good? */
    if ((tmp.p += (int) node->len -1) != tmp.end) {
        x509write_free_node(&tmp);
        return POLARSSL_ERR_X509_POINT_ERROR;
    }

    free(node->data);
    node->data = tmp.data;
    node->p = tmp.p;
    node->end = tmp.end;
    node->len = tmp.len;

    return 0;
}

/*
 * write nodes into a asn.1 object
 */
static int asn1_append_nodes(x509_node *node, int tag, int anz, ...)
{
    va_list ap;
    size_t size = 0;
    x509_node *tmp;
    int count;

    va_start(ap, anz);
    count = anz;

    while (count--) {

        tmp = va_arg(ap, x509_node*);
        if (tmp->data != NULL)
            size += tmp->len;
    }

    if ( size > 127) {
        if (x509write_realloc_node(node, size + (size_t) 2 +
                    asn1_eval_octet(size)) != 0)
            return 1;
    } else {
        if (x509write_realloc_node(node, size + (size_t) 2) != 0)
            return 1;
    }

    /* tag */
    *(node->p) = tag & 0xFF;
    ++node->p;

    /* len */
    asn1_add_len(size, node);

    /* value */
    va_start(ap, anz);
    count = anz;

    while (count--) {

        tmp = va_arg(ap, x509_node*);
        if (tmp->data != NULL) {

            memcpy(node->p, tmp->data, tmp->len);
            if ((node->p += (int) tmp->len -1) != node->end)
                ++node->p;
        }
    }

    va_end(ap);
    return 0;
}

/*
 * write a ASN.1 conform object identifiere include a "tag"
 */
static int asn1_add_oid(x509_node *node, unsigned char *oid, size_t len,
        int tag, int tag_val, unsigned char *value, size_t val_len)
{
    int ret;
    x509_node tmp;

    x509write_init_node(&tmp);

    /* OBJECT IDENTIFIER */
    if ((ret = asn1_add_obj(oid, len, ASN1_OID, &tmp)) != 0) {
        x509write_free_node(&tmp);
        return ret;
    }

    /* value */
    if ((ret = asn1_add_obj(value, val_len, tag_val, &tmp)) != 0) {
        x509write_free_node(&tmp);
        return ret;
    }

    /* SET/SEQUENCE */
    if ((ret = asn1_append_nodes(node, tag, 1, &tmp)) != 0) {
        x509write_free_node(&tmp);
        return ret;
    }

    x509write_free_node(&tmp);
    return 0;
}

/*
 *  utcTime        UTCTime
 */
static int asn1_add_date_utc(unsigned char *time, x509_node *node)
{
    unsigned char date[13], *sp;
    x509_time xtime;
    int ret;

    sscanf((char*)time, "%d-%d-%d %d:%d:%d", &xtime.year, &xtime.mon,
    	&xtime.day, &xtime.hour, &xtime.min, &xtime.sec);

    /* convert to YY */
    if (xtime.year > 2000)
        xtime.year -= 2000;
    else
        xtime.year -= 1900;

    snprintf((char*)date, 13, "%2d%2d%2d%2d%2d%2d", xtime.year, xtime.mon, xtime.day,
        xtime.hour, xtime.min, xtime.sec);

    /* replace ' ' to '0' */
    for (sp = date; *sp != '\0'; ++sp)
        if (*sp == '\x20')
            *sp = '\x30';

    date[12] = 'Z';

    if ((ret = asn1_add_obj(date, 13, ASN1_UTC_TIME, node)) != 0)
        return ret;

    return 0;
}

/*
 * serialize an rsa key into DER
 */

int x509write_serialize_key(rsa_context *rsa, x509_node *node)
{
    int ret = 0;
    x509write_init_node(node);

    /* vers, n, e, d, p, q, dp, dq, pq */
    if ((ret = asn1_add_int(rsa->ver, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->N, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->E, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->D, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->P, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->Q, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->DP, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->DQ, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_add_mpi(&rsa->QP, ASN1_INTEGER, node)) != 0)
        return ret;
    if ((ret = asn1_append_tag(node, ASN1_CONSTRUCTED | ASN1_SEQUENCE)) != 0)
        return ret;

    return 0;
}

/*
 * write a der/pem encoded rsa private key into a file
 */
int x509write_keyfile(rsa_context *rsa, char *path, int out_flag)
{
    int ret = 0;
    const char	key_beg[] = "-----BEGIN RSA PRIVATE KEY-----\n",
                key_end[] = "-----END RSA PRIVATE KEY-----\n";
    x509_node node;

    x509write_init_node(&node);
    if ((ret = x509write_serialize_key(rsa,&node)) != 0) {
        x509write_free_node(&node);
	      return ret;
    }

    ret = x509write_file(&node,path,out_flag,key_beg,key_end);
    x509write_free_node(&node);

    return ret;
}


/*
 * reasize the memory for node
 */
static int x509write_realloc_node(x509_node *node, size_t larger)
{
    /* init len */
    if (node->data == NULL) {
        node->len = 0;
        node->data = malloc(larger);
        if(node->data == NULL)
            return 1;
    } else {
        /* realloc memory */
        if ((node->data = realloc(node->data, node->len + larger)) == NULL)
            return 1;
    }

    /* init pointer */
    node->p = &node->data[node->len];
    node->len += larger;
    node->end = &node->data[node->len -1];

    return 0;
}

/*
 * init node
 */
void x509write_init_node(x509_node *node)
{
    memset(node, 0, sizeof(x509_node));
}

/*
 * clean memory
 */
void x509write_free_node(x509_node *node)
{
    if (node->data != NULL)
        free(node->data);
    node->p = NULL;
    node->end = NULL;
    node->len = 0;
}

/*
 * write a x509 certificate into file
 */
int x509write_crtfile(x509_raw *chain, unsigned char *path, int out_flag)
{
    const char	cer_beg[] = "-----BEGIN CERTIFICATE-----\n",
		cer_end[] = "-----END CERTIFICATE-----\n";

    return x509write_file(&chain->raw, (char*)path, out_flag, cer_beg, cer_end);
}

/*
 * write a x509 certificate into file
 */
int x509write_csrfile(x509_raw *chain, unsigned char *path, int out_flag)
{
    const char	cer_beg[] = "-----BEGIN CERTIFICATE REQUEST-----\n",
		cer_end[] = "-----END CERTIFICATE REQUEST-----\n";

    return x509write_file(&chain->raw, (char*)path, out_flag, cer_beg, cer_end);
}

/*
 * write an x509 file
 */
static int x509write_file(x509_node *node, char *path, int format,
        const char* pem_prolog, const char* pem_epilog)
{
    FILE *ofstream = stdout;
    int is_err = 1, buf_len, i, n;
    unsigned char* base_buf;

    if (path) {
    	if ((ofstream = fopen(path, "wb")) == NULL)
    		return 1;
    }

    switch (format) {
        case X509_OUTPUT_DER:
            if (fwrite(node->data, 1, node->len, ofstream)
                != node->len)
                is_err = -1;
            break;

        case X509_OUTPUT_PEM:
            if (fprintf(ofstream, "%s", pem_prolog)<0) {
                is_err = -1;
                break;
            }

            buf_len = node->len << 1;
            base_buf = (unsigned char*) malloc((size_t)buf_len);
            memset(base_buf,0,buf_len);
            if (base64_encode(base_buf, &buf_len, node->data,
                        (int) node->len) != 0) {
                is_err = -1;
                break;
            }

            n=strlen((char*)base_buf);
            for(i=0;i<n;i+=64) {
                fprintf(ofstream,"%.64s\n",&base_buf[i]);
            }

            if (fprintf(ofstream, "%s", pem_epilog)<0) {
                is_err = -1;
                break;
            }

            free(base_buf);
    }

    fclose(ofstream);

    if (is_err == -1)
        return 1;

    return 0;
}


/*
 * add the owner public key to x509 certificate
 */
int x509write_add_pubkey(x509_raw *chain, rsa_context *pubkey)
{
    x509_node n_tmp, n_tmp2, *node;
    int ret;

    node = &chain->subpubkey;

    x509write_init_node(&n_tmp);
    x509write_init_node(&n_tmp2);

    /*
    *  RSAPublicKey ::= SEQUENCE {
    *      modulus           INTEGER,  -- n
    *      publicExponent    INTEGER   -- e
    *  }
    */
    if ((ret = asn1_add_mpi(&pubkey->N, ASN1_INTEGER, &n_tmp)) != 0) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
        return ret;
    }
    if ((ret = asn1_add_mpi(&pubkey->E, ASN1_INTEGER, &n_tmp)) != 0) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
        return ret;
    }
    if ((ret = asn1_append_tag(&n_tmp, ASN1_CONSTRUCTED | ASN1_SEQUENCE))
            != 0) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
        return ret;
    }

    /*
     *  SubjectPublicKeyInfo  ::=  SEQUENCE  {
     *       algorithm            AlgorithmIdentifier,
     *       subjectPublicKey     BIT STRING }
     */
    if ((ret = asn1_append_tag(&n_tmp, ASN1_BIT_STRING)) != 0) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
       return ret;
    }
    if ((ret = asn1_add_oid(&n_tmp2, (unsigned char*)OID_PKCS1_RSA, 9,
                  ASN1_CONSTRUCTED | ASN1_SEQUENCE, ASN1_NULL,
                  (unsigned char *)"", 0)) != 0) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
        return ret;
    }

    if ((ret = asn1_append_nodes(node, ASN1_CONSTRUCTED | ASN1_SEQUENCE, 2,
                   &n_tmp2, &n_tmp))) {
        x509write_free_node(&n_tmp);
        x509write_free_node(&n_tmp2);
        return ret;
    }

    x509write_free_node(&n_tmp);
    x509write_free_node(&n_tmp2);
    return 0;
}

/*
 *  RelativeDistinguishedName ::=
 *    SET OF AttributeTypeAndValue
 *
 *  AttributeTypeAndValue ::= SEQUENCE {
 *    type     AttributeType,
 *    value    AttributeValue }
 */
static int x509write_add_name(x509_node *node, unsigned char *oid,
        unsigned int oid_len, unsigned char *value, int len, int value_tag)
{
    int ret;
    x509_node n_tmp;

    x509write_init_node(&n_tmp);

    if ((ret = asn1_add_oid(&n_tmp, oid, oid_len,
                ASN1_CONSTRUCTED | ASN1_SEQUENCE, value_tag,
                value, len))) {
        x509write_free_node(&n_tmp);
        return ret;
    }

    if ((asn1_append_nodes(node, ASN1_CONSTRUCTED | ASN1_SET, 1, &n_tmp))
            != 0) {
        x509write_free_node(&n_tmp);
        return ret;
    }

    x509write_free_node(&n_tmp);
    return 0;
}

/*
 * Parse the name string and add to node
 */
static int x509write_parse_names(x509_node *node, unsigned char *names)
{
    unsigned char *sp, *begin = NULL;
    unsigned char oid[3] = OID_X520, tag[4], *tag_sp = tag;
    unsigned char *C = NULL, *CN = NULL, *O = NULL, *OU = NULL,
                  *ST = NULL, *L = NULL, *R = NULL;
    int C_len = 0, CN_len = 0, O_len = 0, OU_len = 0, ST_len = 0,
		L_len = 0, R_len = 0;
    int ret = 0, is_tag = 1, is_begin = -1, len = 0;


    for (sp = names; ; ++sp) {

        /* filter tag */
        if (is_tag == 1) {

            if (tag_sp == &tag[3])
                return POLARSSL_ERR_X509_VALUE_TO_LENGTH;

            /* is tag end? */
            if (*sp == '=') {
                is_tag = -1;
                *tag_sp = '\0';
                is_begin = 1;
                /* set len 0 (reset) */
                len = 0;
            } else {
                /* tag hasn't ' '! */
                if (*sp != ' ') {
                    *tag_sp = *sp;
                    ++tag_sp;
                }
            }
        /* filter value */
        } else {

            /* set pointer of value begin */
            if (is_begin == 1) {
                begin = sp;
                is_begin = -1;
            }

            /* is value at end? */
            if (*sp == ';' or *sp == '\0') {
                is_tag = 1;

                /* common name */
                if (tag[0] == 'C' and tag[1] == 'N') {
                    CN = begin;
                    CN_len = len;

                /* organization */
                } else if (tag[0] == 'O' and tag[1] == '\0') {
                    O = begin;
                    O_len = len;

                /* country */
                } else if (tag[0] == 'C' and tag[1] == '\0') {
                    C = begin;
                    C_len = len;

                /* organisation unit */
                } else if (tag[0] == 'O' and tag[1] == 'U') {
                    OU = begin;
                    OU_len = len;

                /* state */
                } else if (tag[0] == 'S' and tag[1] == 'T') {
                    ST = begin;
                    ST_len = len;

                /* locality */
                } else if (tag[0] == 'L' and tag[1] == '\0') {
                    L = begin;
                    L_len = len;

                /* email */
                } else if (tag[0] == 'R' and tag[1] == '\0') {
                    R = begin;
                    R_len = len;
                }

                /* set tag poiner to begin */
                tag_sp = tag;

                /* is at end? */
                if (*sp == '\0' or *(sp +1) == '\0')
                    break;
            } else {
                ++len;
            }
        }

        /* make saver */
        if (*sp == '\0')
          break;
    } /* end for */

    /* country */
    if (C != NULL) {
        oid[2] = X520_COUNTRY;
        if ((ret = x509write_add_name(node, oid, 3, C, C_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* state */
    if (ST != NULL) {
        oid[2] = X520_STATE;
        if ((ret = x509write_add_name(node, oid, 3, ST, ST_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* locality */
    if (L != NULL) {
        oid[2] = X520_LOCALITY;
        if ((ret = x509write_add_name(node, oid, 3, L, L_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* organization */
    if (O != NULL) {
        oid[2] = X520_ORGANIZATION;
        if ((ret = x509write_add_name(node, oid, 3, O, O_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* organisation unit */
    if (OU != NULL) {
        oid[2] = X520_ORG_UNIT;
        if ((ret = x509write_add_name(node, oid, 3, OU, OU_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* common name */
    if (CN != NULL) {
        oid[2] = X520_COMMON_NAME;
        if ((ret = x509write_add_name(node, oid, 3, CN, CN_len,
                        ASN1_PRINTABLE_STRING)) != 0)
            return ret;
    }

    /* email */
    if (R != NULL) {
        if ((ret = x509write_add_name(node, (unsigned char*)OID_PKCS9_EMAIL,
        		9, R, R_len, ASN1_IA5_STRING)) != 0)
            return ret;
    }

    if ((asn1_append_tag(node, ASN1_CONSTRUCTED | ASN1_SEQUENCE)) != 0)
        return ret;

    return 0;
}

/*
 * Copy raw data from orginal ca to node
 */
static int x509write_copy_from_raw(x509_node *node, x509_buf *raw)
{
    if (x509write_realloc_node(node, raw->len) != 0)
        return 1;

    memcpy(node->p, raw->p, (size_t)raw->len);
    if ((node->p += raw->len -1) != node->end)
        return POLARSSL_ERR_X509_POINT_ERROR;

    return 0;
}

/*
 * Add the issuer
 */

int x509write_add_issuer(x509_raw *crt, unsigned char *issuer)
{
    return x509write_parse_names(&crt->issuer, issuer);
}

/*
 * Add the subject
 */
int x509write_add_subject(x509_raw *crt, unsigned char *subject)
{
    return x509write_parse_names(&crt->subject, subject);
}

/*
 * Copy issuer line from another cert to issuer
 */
int x509write_copy_issuer(x509_raw *crt, x509_cert *from_crt)
{
    return x509write_copy_from_raw(&crt->issuer, &from_crt->issuer_raw);
}

/*
 * Copy subject line from another cert
 */
int x509write_copy_subject(x509_raw *crt, x509_cert *from_crt)
{
    return x509write_copy_from_raw(&crt->subject, &from_crt->subject_raw);
}

/*
 * Copy subject line form antoher cert into issuer
 */
int x509write_copy_issuer_form_subject(x509_raw *crt,
        x509_cert *from_crt)
{
    return x509write_copy_from_raw(&crt->issuer, &from_crt->subject_raw);
}

/*
 * Copy issuer line from another cert into subject
 */
int x509write_copy_subject_from_issuer(x509_raw *crt,
        x509_cert * from_crt)
{
    return x509write_copy_from_raw(&crt->subject, &from_crt->issuer_raw);
}

/*
 *  Validity ::= SEQUENCE {
 *       notBefore      Time,
 *       notAfter       Time }
 *
 *  Time ::= CHOICE {
 *       utcTime        UTCTime,
 *       generalTime    GeneralizedTime }
 */
/* TODO: No handle GeneralizedTime! */
int x509write_add_validity(x509_raw *chain, unsigned char *befor,
        unsigned char *after)
{
    int ret;

    x509_node *node = &chain->validity;

    /* notBefore */
    if ((ret = asn1_add_date_utc(befor, node)) != 0)
        return ret;

    /* notAfter */
    if ((ret = asn1_add_date_utc(after, node)) != 0)
        return ret;

    if ((ret = asn1_append_tag(node, ASN1_CONSTRUCTED | ASN1_SEQUENCE)) != 0)
        return ret;

    return 0;
}

/*
 * make hash from tbs and sign that with private key
 */
static int x509write_make_sign(x509_raw *chain, rsa_context *privkey)
{
    int ret;
    unsigned char hash[20], *sign;
    size_t sign_len = (size_t) mpi_size(&privkey->N);

    /* make hash */
    sha1(chain->tbs.data, chain->tbs.len, hash);

    /* create sign */
    sign = (unsigned char *) malloc(sign_len);
    if (sign == NULL)
        return 1;

    if ((ret = rsa_pkcs1_sign(privkey, RSA_PRIVATE, RSA_SHA1, 20, hash,
                    sign)) != 0)
        return ret;

    if ((ret = asn1_add_obj(sign, sign_len, ASN1_BIT_STRING,
                    &chain->sign)) != 0)
        return ret;

    /*
     *  AlgorithmIdentifier  ::=  SEQUENCE  {
     *       algorithm               OBJECT IDENTIFIER,
     *       parameters              ANY DEFINED BY algorithm OPTIONAL  }
     */
    return asn1_add_oid(&chain->signalg, (unsigned char*)OID_PKCS1_RSA_SHA, 9,
                  ASN1_CONSTRUCTED | ASN1_SEQUENCE, ASN1_NULL,
                  (unsigned char*)"", 0);
}

/*
 * Create a random serial
 */
static int get_random_serial(void)
{
    int random = 0;
    FILE *fd;

    fd = fopen("/dev/urandom", "r");

    if (fd) {
	if (fread(&random, 1, sizeof(random), fd) != sizeof(random))
            random = 0;

        fclose(fd);
    }

    return random;
}

/*
 * Create a self signed certificate
 */
int x509write_create_sign(x509_raw *chain, rsa_context *privkey)
{
    int ret, serial;

    /*
     *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
     */
    if ((ret = asn1_add_int(2, &chain->version)) != 0)
        return ret;

    if ((ret = asn1_append_tag(&chain->version, ASN1_CONTEXT_SPECIFIC |
                    ASN1_CONSTRUCTED)) != 0)
        return ret;


    /*
     *  CertificateSerialNumber  ::=  INTEGER
     */
    serial = get_random_serial();

    if (serial == 0)
        return 1;

    if ((ret = asn1_add_int(serial, &chain->serial)) != 0)
        return ret;

    /*
     *  AlgorithmIdentifier  ::=  SEQUENCE  {
     *       algorithm               OBJECT IDENTIFIER,
     *       parameters              ANY DEFINED BY algorithm OPTIONAL  }
     */
    if ((ret = asn1_add_oid(&chain->tbs_signalg,
				(unsigned char*)OID_PKCS1_RSA_SHA, 9, ASN1_CONSTRUCTED |
				ASN1_SEQUENCE, ASN1_NULL, (unsigned char*)"", 0)) != 0)
        return ret;

   /*
    *  Create the tbs
    */
    if ((ret = asn1_append_nodes(&chain->tbs, ASN1_CONSTRUCTED |
                    ASN1_SEQUENCE, 7, &chain->version, &chain->serial,
                    &chain->tbs_signalg, &chain->issuer, &chain->validity,
                    &chain->subject, &chain->subpubkey)) != 0)
        return ret;

    /* make signing */
    if ((ret = x509write_make_sign(chain, privkey)) != 0)
        return ret;

    /* finishing */
    if ((ret = asn1_append_nodes(&chain->raw, ASN1_CONSTRUCTED |
                    ASN1_SEQUENCE, 3, &chain->tbs, &chain->signalg,
                    &chain->sign)) != 0)
        return ret;

    return 0;
}

int x509write_create_selfsign(x509_raw *chain, rsa_context *privkey)
{
    /*
     * On self signed certificate are subject and issuer the same
     */
    x509write_free_node(&chain->issuer);
    chain->issuer = chain->subject;
    return x509write_create_sign(chain, privkey);
}

/*
 * CertificationRequestInfo ::= SEQUENCE                    {
 *    version                       Version,
 *    subject                       Name,
 *    subjectPublicKeyInfo          SubjectPublicKeyInfo,
 *    attributes                    [0] IMPLICIT Attributes }
 *
 * CertificationRequest ::=   SEQUENCE                      {
 *    certificationRequestInfo  CertificationRequestInfo,
 *    signatureAlgorithm        SignatureAlgorithmIdentifier,
 *    signature                 Signature                   }
 *
 * It use chain.serail for attributes!
 *
 */
int x509write_create_csr(x509_raw *chain, rsa_context *privkey)
{
    int ret;

    /* version ::= INTEGER */
    if ((ret = asn1_add_int(0, &chain->version)) != 0)
        return ret;

    /* write attributes */
    if ((ret = asn1_add_obj((unsigned char*)"", 0, ASN1_CONTEXT_SPECIFIC |
                    ASN1_CONSTRUCTED, &chain->serial)) != 0)
        return ret;

    /* create CertificationRequestInfo */
    if ((ret = asn1_append_nodes(&chain->tbs, ASN1_CONSTRUCTED |
                    ASN1_SEQUENCE, 4, &chain->version, &chain->subject,
                    &chain->subpubkey, &chain->serial)) != 0)
        return ret;

    /* make signing */
    if ((ret = x509write_make_sign(chain, privkey)) != 0)
        return ret;

    /* finish */
    if ((ret = asn1_append_nodes(&chain->raw, ASN1_CONSTRUCTED | ASN1_SEQUENCE,
                    3, &chain->tbs, &chain->signalg, &chain->sign)) != 0)
        return ret;

    return ret;
}

/*
 * Free memory
 */
void x509write_free_raw(x509_raw *chain)
{
    x509write_free_node(&chain->raw);
    x509write_free_node(&chain->tbs);
    x509write_free_node(&chain->version);
    x509write_free_node(&chain->serial);
    x509write_free_node(&chain->tbs_signalg);
    x509write_free_node(&chain->issuer);
    x509write_free_node(&chain->validity);
    if (chain->subject.data != chain->issuer.data)
        x509write_free_node(&chain->subject);
    x509write_free_node(&chain->subpubkey);
    x509write_free_node(&chain->signalg);
    x509write_free_node(&chain->sign);
}

void x509write_init_raw(x509_raw *chain)
{
    memset((void *) chain, 0, sizeof(x509_raw));
}

