/* asn.c
 *
 * Copyright (C) 2006-2009 Sawtooth Consulting Ltd.
 *
 * This file is part of CyaSSL.
 *
 * CyaSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * CyaSSL 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */


#ifdef THREADX
    #include "os.h"           /* dc_rtc_api needs    */
    #include "dc_rtc_api.h"   /* to get current time */
#endif
#include "asn.h"
#include "coding.h"
#include "ctc_sha.h"
#include "ctc_md5.h"
#include "error.h"

#ifdef HAVE_NTRU
    #include "crypto_ntru.h"
#endif


#ifdef _MSC_VER
    /* 4996 warning to use MS extensions e.g., strcpy_s instead of XSTRNCPY */
    #pragma warning(disable: 4996)
#endif


#ifndef TRUE
enum {
    FALSE = 0,
    TRUE  = 1
};
#endif

enum {
    ISSUER  = 0,
    SUBJECT = 1,

    BEFORE  = 0,
    AFTER   = 1
};


#ifdef THREADX
    /* uses parital <time.h> structures */
    #define XTIME(tl)  (0)
    #define XGMTIME(c) my_gmtime((c))
    #define XVALIDATE_DATE(d, f, t) ValidateDate((d), (f), (t))
#elif defined(MICRIUM)
    #include <clk.h>
    #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
        #define XVALIDATE_DATE(d, f, t) NetSecure_ValidDate((d), (f), (t))
    #else
        #define XVALIDATE_DATE(d, f, t) (0)
    #endif
    #define NO_TIME_H
    /* since Micrium not defining XTIME or XGMTIME, CERT_GEN not available */
#elif defined(USER_TIME)
    /* no <time.h> strucutres used */
    #define NO_TIME_H
    /* user time, and gmtime compatible functions, there is a gmtime 
       implementation here that WINCE uses, so really just need some ticks
       since the EPOCH 
    */
#else
    /* default */
    /* uses complete <time.h> facility */
    #include <time.h> 
    #define XTIME(tl)  time((tl))
    #define XGMTIME(c) gmtime((c))
    #define XVALIDATE_DATE(d, f, t) ValidateDate((d), (f), (t))
#endif


#ifdef _WIN32_WCE
/* no time() or gmtime() even though in time.h header?? */

#include <windows.h>


time_t time(time_t* timer)
{
    SYSTEMTIME     sysTime;
    FILETIME       fTime;
    ULARGE_INTEGER intTime;
    time_t         localTime;

    if (timer == NULL)
        timer = &localTime;

    GetSystemTime(&sysTime);
    SystemTimeToFileTime(&sysTime, &fTime);
    
    XMEMCPY(&intTime, &fTime, sizeof(FILETIME));
    /* subtract EPOCH */
    intTime.QuadPart -= 0x19db1ded53e8000;
    /* to secs */
    intTime.QuadPart /= 10000000;
    *timer = (time_t)intTime.QuadPart;

    return *timer;
}



struct tm* gmtime(const time_t* timer)
{
    #define YEAR0          1900
    #define EPOCH_YEAR     1970
    #define SECS_DAY       (24L * 60L * 60L)
    #define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) %400)))
    #define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)

    static const int _ytab[2][12] =
    {
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
        {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    };

    static struct tm st_time;
    struct tm* ret = &st_time;
    time_t time = *timer;
    unsigned long dayclock, dayno;
    int year = EPOCH_YEAR;

    dayclock = (unsigned long)time % SECS_DAY;
    dayno    = (unsigned long)time / SECS_DAY;

    ret->tm_sec  =  dayclock % 60;
    ret->tm_min  = (dayclock % 3600) / 60;
    ret->tm_hour =  dayclock / 3600;
    ret->tm_wday = (dayno + 4) % 7;        /* day 0 a Thursday */

    while(dayno >= (unsigned long)YEARSIZE(year)) {
        dayno -= YEARSIZE(year);
        year++;
    }

    ret->tm_year = year - YEAR0;
    ret->tm_yday = dayno;
    ret->tm_mon  = 0;

    while(dayno >= (unsigned long)_ytab[LEAPYEAR(year)][ret->tm_mon]) {
        dayno -= _ytab[LEAPYEAR(year)][ret->tm_mon];
        ret->tm_mon++;
    }

    ret->tm_mday  = ++dayno;
    ret->tm_isdst = 0;

    return ret;
}

#endif /* _WIN32_WCE */



#ifdef  THREADX

#define YEAR0          1900

struct tm* my_gmtime(const time_t* timer)       /* has a gmtime() but hangs */
{
    static struct tm st_time;
    struct tm* ret = &st_time;

    DC_RTC_CALENDAR cal;
    dc_rtc_time_get(&cal, TRUE);

    ret->tm_year  = cal.year - YEAR0;       /* gm starts at 1900 */
    ret->tm_mon   = cal.month - 1;          /* gm starts at 0 */
    ret->tm_mday  = cal.day;
    ret->tm_hour  = cal.hour;
    ret->tm_min   = cal.minute;
    ret->tm_sec   = cal.second;

    return ret;
}

#endif /* THREADX */


static INLINE word32 btoi(byte b)
{
    return b - 0x30;
}


/* two byte date/time, add to value */
static INLINE void GetTime(int* value, const byte* date, int* idx)
{
    int i = *idx;

    *value += btoi(date[i++]) * 10;
    *value += btoi(date[i++]);

    *idx = i;
}


#if defined(MICRIUM)

static int NetSecure_ValidDate(CPU_INT08U *date, CPU_INT08U format,
                               CPU_INT08U dateType)
{
    CLK_DATE_TIME   cert_date_time;
    CLK_TS_SEC      cert_ts_sec;
    CLK_TS_SEC      local_ts_sec;
    CPU_INT32S      i;
    CPU_INT32S      val;

    local_ts_sec = Clk_GetTS();
    XMEMSET(&cert_date_time, 0, sizeof(cert_date_time));

    i = 0;
    if (format == ASN_UTC_TIME) {
        if (btoi(date[0]) >= 5)
            cert_date_time.Yr = 1900;
        else
            cert_date_time.Yr = 2000;
    }
    else  { /* format == GENERALIZED_TIME */
        cert_date_time.Yr += btoi(date[i++]) * 1000;
        cert_date_time.Yr += btoi(date[i++]) * 100;
    }

    val = cert_date_time.Yr;
    GetTime(&val,   date, &i);
    cert_date_time.Yr =    (CLK_YR)val;

    val = 0;
    GetTime(&val, date, &i);   
    cert_date_time.Month = (CLK_MONTH)val;
  
    val = 0;
    GetTime(&val, date, &i);  
    cert_date_time.Day =   (CLK_DAY)val;

    val = 0;
    GetTime(&val, date, &i);  
    cert_date_time.Hr =    (CLK_HR)val;
    
    val = 0;
    GetTime(&val, date, &i);  
    cert_date_time.Min =   (CLK_MIN)val;
    
    val = 0;
    GetTime(&val, date, &i);  
    cert_date_time.Sec =   (CLK_SEC)val;

    if (date[i] != 'Z')     /* only Zulu supported for this profile */
        return 0;

    cert_date_time.DayOfWk = 1;
    cert_date_time.DayOfYr = 1;
    Clk_DateTimeToTS(&cert_ts_sec, &cert_date_time);


    if (dateType == BEFORE) {
        if (local_ts_sec < cert_ts_sec)  /* If cert creation date after
                                             current date...  */
            return (DEF_FAIL);           /* ... report an error. */
   } else {
        if (local_ts_sec > cert_ts_sec)  /* If cert expiration date before
                                           current date...  */
            return (DEF_FAIL);           /* ... report an error. */
   }

    return (DEF_OK);
}

#endif /* MICRIUM */


int GetLength(const byte* input, word32* inOutIdx, int* len)
{
    int     length = 0;
    word32  i = *inOutIdx;

    byte b = input[i++];
    if (b >= ASN_LONG_LENGTH) {        
        word32 bytes = b & 0x7F;

        while (bytes--) {
            b = input[i++];
            length = (length << 8) | b;
        }
    }
    else
        length = b;
    
    *inOutIdx = i;
    *len      = length;

    return length;
}


int GetSequence(const byte* input, word32* inOutIdx, int* len)
{
    int    length = -1;
    word32 idx    = *inOutIdx;

    if (input[idx++] != (ASN_SEQUENCE | ASN_CONSTRUCTED) ||
            GetLength(input, &idx, &length) < 0)
        return ASN_PARSE_E;

    *len      = length;
    *inOutIdx = idx;

    return length;
}


int GetSet(const byte* input, word32* inOutIdx, int* len)
{
    int    length = -1;
    word32 idx    = *inOutIdx;

    if (input[idx++] != (ASN_SET | ASN_CONSTRUCTED) ||
            GetLength(input, &idx, &length) < 0)
        return ASN_PARSE_E;

    *len      = length;
    *inOutIdx = idx;

    return length;
}


/* winodws header clash for WinCE using GetVersion */
int GetMyVersion(const byte* input, word32* inOutIdx, int* version)
{
    word32 idx = *inOutIdx;

    if (input[idx++] != ASN_INTEGER)
        return ASN_PARSE_E;

    if (input[idx++] != 0x01)
        return ASN_VERSION_E;

    *version  = input[idx++];
    *inOutIdx = idx;

    return *version;
}


/* May not have one, not an error */
int GetExplicitVersion(const byte* input, word32* inOutIdx, int* version)
{
    word32 idx = *inOutIdx;

    if (input[idx++] == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) {
        *inOutIdx = ++idx;  /* eat header */
        return GetMyVersion(input, inOutIdx, version);
    }

    /* go back as is */
    *version = 0;

    return 0;
}


int GetInt(mp_int* mpi, const byte* input, word32* inOutIdx )
{
    word32 i = *inOutIdx;
    byte   b = input[i++];
    int    length;

    if (b != ASN_INTEGER)
        return ASN_PARSE_E;

    if (GetLength(input, &i, &length) < 0)
        return ASN_PARSE_E;

    if ( (b = input[i++]) == 0x00)
        length--;
    else
        i--;

    mp_init(mpi); 
    if (mp_read_unsigned_bin(mpi, (byte*)input + i, length) != 0) {
        mp_clear(mpi);
        return ASN_GETINT_E;
    }

    *inOutIdx = i + length;
    return 0;
}


static int GetAlgoId(const byte* input, word32* inOutIdx, word32* oid)
{
    int    length;
    word32 i = *inOutIdx;
    byte   b;
    *oid = 0;
    
    if (GetSequence(input, &i, &length) < 0)
        return ASN_PARSE_E;
    
    b = input[i++];
    if (b != ASN_OBJECT_ID) 
        return ASN_OBJECT_ID_E;
    
    if (GetLength(input, &i, &length) < 0)
        return ASN_PARSE_E;
    
    while(length--)
        *oid += input[i++];
    /* just sum it up for now */
    
    /* could have NULL tag and 0 terminator, but may not */
    b = input[i++];
    
    if (b == ASN_TAG_NULL) {
        b = input[i++];
        if (b != 0) 
            return ASN_EXPECT_0_E;
    }
    else
    /* go back, didn't have it */
        i--;
    
    *inOutIdx = i;
    
    return 0;
}


int RsaPrivateKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key,
                        word32 inSz)
{
    word32 begin = *inOutIdx;
    int    version, length;

    if (GetSequence(input, inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (inSz - (*inOutIdx - begin)))
        return ASN_INPUT_E;

    if (GetMyVersion(input, inOutIdx, &version) < 0)
        return ASN_PARSE_E;

    key->type = RSA_PRIVATE;

    if (GetInt(&key->n,  input, inOutIdx) < 0 ||
        GetInt(&key->e,  input, inOutIdx) < 0 ||
        GetInt(&key->d,  input, inOutIdx) < 0 ||
        GetInt(&key->p,  input, inOutIdx) < 0 ||
        GetInt(&key->q,  input, inOutIdx) < 0 ||
        GetInt(&key->dP, input, inOutIdx) < 0 ||
        GetInt(&key->dQ, input, inOutIdx) < 0 ||
        GetInt(&key->u,  input, inOutIdx) < 0 )  return ASN_RSA_KEY_E;

    return 0;
}


/* Remove PKCS8 header, move beginning of traditional to beginning of input */
int ToTraditional(byte* input, word32 sz)
{
    word32 inOutIdx = 0, oid;
    int    version, length;

    if (GetSequence(input, &inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (sz - inOutIdx))
        return ASN_INPUT_E;

    if (GetMyVersion(input, &inOutIdx, &version) < 0)
        return ASN_PARSE_E;
    
    if (GetAlgoId(input, &inOutIdx, &oid) < 0)
        return ASN_PARSE_E;
    
    if (input[inOutIdx++] != ASN_OCTET_STRING)
        return ASN_PARSE_E;
    
    if (GetLength(input, &inOutIdx, &length) < 0)
        return ASN_PARSE_E;
    
    if ((word32)length > (sz - inOutIdx))
        return ASN_INPUT_E;
    
    XMEMMOVE(input, input + inOutIdx, length);

    return 0;
}


int RsaPublicKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key,
                       word32 inSz)
{
    word32 begin = *inOutIdx;
    int    length;
    byte   b;

    if (GetSequence(input, inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (inSz - (*inOutIdx - begin)))
        return ASN_INPUT_E;

    key->type = RSA_PUBLIC;
    b = input[*inOutIdx];

#ifdef OPENSSL_EXTRA    
    if (b != ASN_INTEGER) {
        /* not from decoded cert, will have algo id, skip past */
        if (GetSequence(input, inOutIdx, &length) < 0)
            return ASN_PARSE_E;
        
        b = input[(*inOutIdx)++];
        if (b != ASN_OBJECT_ID) 
            return ASN_OBJECT_ID_E;
        
        if (GetLength(input, inOutIdx, &length) < 0)
            return ASN_PARSE_E;
        
        *inOutIdx += length;   /* skip past */
        
        /* could have NULL tag and 0 terminator, but may not */
        b = input[(*inOutIdx)++];
        
        if (b == ASN_TAG_NULL) {
            b = input[(*inOutIdx)++];
            if (b != 0) 
                return ASN_EXPECT_0_E;
        }
        else
        /* go back, didn't have it */
            (*inOutIdx)--;
        
        /* should have bit tag length and seq next */
        b = input[(*inOutIdx)++];
        if (b != ASN_BIT_STRING)
            return ASN_BITSTR_E;
        
        if (GetLength(input, inOutIdx, &length) < 0)
            return ASN_PARSE_E;
        
        /* could have 0 */
        b = input[(*inOutIdx)++];
        if (b != 0)
            (*inOutIdx)--;
        
        if (GetSequence(input, inOutIdx, &length) < 0)
            return ASN_PARSE_E;
    }
#endif /* OPENSSL_EXTRA */

    if (GetInt(&key->n,  input, inOutIdx) < 0 ||
        GetInt(&key->e,  input, inOutIdx) < 0 )  return ASN_RSA_KEY_E;

    return 0;
}


#ifndef NO_DH

int DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz)
{
    word32 begin = *inOutIdx;
    int    length;

    if (GetSequence(input, inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (inSz - (*inOutIdx - begin)))
        return ASN_INPUT_E;

    if (GetInt(&key->p,  input, inOutIdx) < 0 ||
        GetInt(&key->g,  input, inOutIdx) < 0 )  return ASN_DH_KEY_E;

    return 0;
}

int DhSetKey(DhKey* key, const byte* p, word32 pSz, const byte* g, word32 gSz)
{
    /* may have leading 0 */
    if (p[0] == 0) {
        pSz--; p++;
    }

    if (g[0] == 0) {
        gSz--; g++;
    }

    mp_init(&key->p);
    if (mp_read_unsigned_bin(&key->p, p, pSz) != 0) {
        mp_clear(&key->p);
        return ASN_DH_KEY_E;
    }

    mp_init(&key->g);
    if (mp_read_unsigned_bin(&key->g, g, gSz) != 0) {
        mp_clear(&key->p);
        return ASN_DH_KEY_E;
    }

    return 0;
}


#endif /* NO_DH */


#ifndef NO_DSA

int DsaPublicKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                        word32 inSz)
{
    word32 begin = *inOutIdx;
    int    length;

    if (GetSequence(input, inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (inSz - (*inOutIdx - begin)))
        return ASN_INPUT_E;

    if (GetInt(&key->p,  input, inOutIdx) < 0 ||
        GetInt(&key->q,  input, inOutIdx) < 0 ||
        GetInt(&key->g,  input, inOutIdx) < 0 ||
        GetInt(&key->y,  input, inOutIdx) < 0 )  return ASN_DH_KEY_E;

    key->type = DSA_PUBLIC;
    return 0;
}


int DsaPrivateKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                        word32 inSz)
{
    word32 begin = *inOutIdx;
    int    length, version;

    if (GetSequence(input, inOutIdx, &length) < 0)
        return ASN_PARSE_E;

    if ((word32)length > (inSz - (*inOutIdx - begin)))
        return ASN_INPUT_E;

    if (GetMyVersion(input, inOutIdx, &version) < 0)
        return ASN_PARSE_E;

    if (GetInt(&key->p,  input, inOutIdx) < 0 ||
        GetInt(&key->q,  input, inOutIdx) < 0 ||
        GetInt(&key->g,  input, inOutIdx) < 0 ||
        GetInt(&key->y,  input, inOutIdx) < 0 ||
        GetInt(&key->x,  input, inOutIdx) < 0 )  return ASN_DH_KEY_E;

    key->type = DSA_PRIVATE;
    return 0;
}

#endif /* NO_DSA */


void InitDecodedCert(DecodedCert* cert, byte* source, void* heap)
{
    cert->publicKey       = 0;
    cert->pubKeyStored    = 0;
    cert->signature       = 0;
    cert->subjectCN       = 0;
    cert->subjectCNLen    = 0;
    cert->source          = source;  /* don't own */
    cert->srcIdx          = 0;
    cert->heap            = heap;
#ifdef CYASSL_CERT_GEN
    cert->subjectSN       = 0;
    cert->subjectSNLen    = 0;
    cert->subjectC        = 0;
    cert->subjectCLen     = 0;
    cert->subjectL        = 0;
    cert->subjectLLen     = 0;
    cert->subjectST       = 0;
    cert->subjectSTLen    = 0;
    cert->subjectO        = 0;
    cert->subjectOLen     = 0;
    cert->subjectOU       = 0;
    cert->subjectOULen    = 0;
    cert->subjectEmail    = 0;
    cert->subjectEmailLen = 0;
#endif /* CYASSL_CERT_GEN */
}


void FreeDecodedCert(DecodedCert* cert)
{
    if (cert->subjectCNLen == 0)  /* 0 means no longer pointer to raw, we own */
        XFREE(cert->subjectCN, cert->heap, DYNAMIC_TYPE_SUBJECT_CN);
    if (cert->pubKeyStored == 1)
        XFREE(cert->publicKey, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
}


static int GetCertHeader(DecodedCert* cert, word32 inSz)
{
    int    ret = 0, version, len;
    word32 begin = cert->srcIdx;
    mp_int mpi;

    if (GetSequence(cert->source, &cert->srcIdx, &len) < 0)
        return ASN_PARSE_E;

    if ((word32)len > (inSz - (cert->srcIdx - begin))) return ASN_INPUT_E;

    cert->certBegin = cert->srcIdx;

    GetSequence(cert->source, &cert->srcIdx, &len);
    cert->sigIndex = len + cert->srcIdx;

    if (GetExplicitVersion(cert->source, &cert->srcIdx, &version) < 0)
        return ASN_PARSE_E;

    if (GetInt(&mpi, cert->source, &cert->srcIdx) < 0) 
        ret = ASN_PARSE_E;

    mp_clear(&mpi);
    return ret;
}


static int StoreKey(DecodedCert* cert)
{
    int    length;
    word32 read = cert->srcIdx;

    if (cert->keyOID == NTRUk)
        return 0;                /* already stored */

    if (GetSequence(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;
   
    read = cert->srcIdx - read;
    length += read;

    while (read--)
       cert->srcIdx--;

    cert->pubKeySize = length;
    cert->publicKey = cert->source + cert->srcIdx;
    cert->srcIdx += length;

    return 0;
}


static int GetKey(DecodedCert* cert)
{
    int length;
#ifdef HAVE_NTRU
    int tmpIdx = cert->srcIdx;
#endif

    if (GetSequence(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;
    
    if (GetAlgoId(cert->source, &cert->srcIdx, &cert->keyOID) < 0)
        return ASN_PARSE_E;

    if (cert->keyOID == RSAk) {
        byte b = cert->source[cert->srcIdx++];
        if (b != ASN_BIT_STRING)
            return ASN_BITSTR_E;

        if (GetLength(cert->source, &cert->srcIdx, &length) < 0)
            return ASN_PARSE_E;
        b = cert->source[cert->srcIdx++];
        if (b != 0x00)
            return ASN_EXPECT_0_E;
    }
    else if (cert->keyOID == DSAk )
        ;   /* do nothing */
#ifdef HAVE_NTRU
    else if (cert->keyOID == NTRUk ) {
        const byte* key = &cert->source[tmpIdx];
        byte*       next = (byte*)key;
        word16      keyLen;
        byte        keyBlob[MAX_NTRU_KEY_SZ];

        word32 rc = crypto_ntru_encrypt_subjectPublicKeyInfo2PublicKey(key,
                            &keyLen, NULL, &next);

        if (rc != NTRU_OK)
            return ASN_NTRU_KEY_E;
        if (keyLen > sizeof(keyBlob))
            return ASN_NTRU_KEY_E;

        rc = crypto_ntru_encrypt_subjectPublicKeyInfo2PublicKey(key, &keyLen,
                                                                keyBlob, &next);
        if (rc != NTRU_OK)
            return ASN_NTRU_KEY_E;

        if ( (next - key) < 0)
            return ASN_NTRU_KEY_E;

        cert->srcIdx = tmpIdx + (next - key);

        cert->publicKey = (byte*) XMALLOC(keyLen, cert->heap,
                                          DYNAMIC_TYPE_PUBLIC_KEY);
        if (cert->publicKey == NULL)
            return MEMORY_E;
        memcpy(cert->publicKey, keyBlob, keyLen);
        cert->pubKeyStored = 1;
        cert->pubKeySize   = keyLen;
    }
#endif
    else
        return ASN_UNKNOWN_OID_E;
    
    return StoreKey(cert);
}


/* process NAME, either issuer or subject */
static int GetName(DecodedCert* cert, int nameType)
{
    Sha    sha;
    int    length;  /* length of all distinguished names */
    int    dummy;
    char* full = (nameType == ISSUER) ? cert->issuer : cert->subject;
    word32 idx = 0;

    InitSha(&sha);

    if (GetSequence(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;

    length += cert->srcIdx;

    while (cert->srcIdx < (word32)length) {
        byte   b;
        byte   joint[2];
        int    oidSz;

        if (GetSet(cert->source, &cert->srcIdx, &dummy) < 0)
            return ASN_PARSE_E;

        if (GetSequence(cert->source, &cert->srcIdx, &dummy) < 0)
            return ASN_PARSE_E;

        b = cert->source[cert->srcIdx++];
        if (b != ASN_OBJECT_ID) 
            return ASN_OBJECT_ID_E;

        if (GetLength(cert->source, &cert->srcIdx, &oidSz) < 0)
            return ASN_PARSE_E;

        XMEMCPY(joint, &cert->source[cert->srcIdx], sizeof(joint));

        /* v1 name types */
        if (joint[0] == 0x55 && joint[1] == 0x04) {
            byte   id;
            byte   copy = FALSE;
            int    strLen;

            cert->srcIdx += 2;
            id = cert->source[cert->srcIdx++]; 
            b  = cert->source[cert->srcIdx++];    /* strType */

            if (GetLength(cert->source, &cert->srcIdx, &strLen) < 0)
                return ASN_PARSE_E;

            if (strLen > (int)(ASN_NAME_MAX - idx))
                return ASN_PARSE_E; 

            if (4  > (ASN_NAME_MAX - idx))  /* make sure room for biggest */
                return ASN_PARSE_E;         /* pre fix header too "/CN=" */

            if (id == ASN_COMMON_NAME) {
                if (nameType == SUBJECT) {
                    cert->subjectCN = (char *)&cert->source[cert->srcIdx];
                    cert->subjectCNLen = strLen;
                }

                XMEMCPY(&full[idx], "/CN=", 4);
                idx += 4;
                copy = TRUE;
            }
            else if (id == ASN_SUR_NAME) {
                XMEMCPY(&full[idx], "/SN=", 4);
                idx += 4;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectSN = (char*)&cert->source[cert->srcIdx];
                    cert->subjectSNLen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }
            else if (id == ASN_COUNTRY_NAME) {
                XMEMCPY(&full[idx], "/C=", 3);
                idx += 3;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectC = (char*)&cert->source[cert->srcIdx];
                    cert->subjectCLen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }
            else if (id == ASN_LOCALITY_NAME) {
                XMEMCPY(&full[idx], "/L=", 3);
                idx += 3;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectL = (char*)&cert->source[cert->srcIdx];
                    cert->subjectLLen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }
            else if (id == ASN_STATE_NAME) {
                XMEMCPY(&full[idx], "/ST=", 4);
                idx += 4;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectST = (char*)&cert->source[cert->srcIdx];
                    cert->subjectSTLen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }
            else if (id == ASN_ORG_NAME) {
                XMEMCPY(&full[idx], "/O=", 3);
                idx += 3;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectO = (char*)&cert->source[cert->srcIdx];
                    cert->subjectOLen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }
            else if (id == ASN_ORGUNIT_NAME) {
                XMEMCPY(&full[idx], "/OU=", 4);
                idx += 4;
                copy = TRUE;
#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectOU = (char*)&cert->source[cert->srcIdx];
                    cert->subjectOULen = strLen;
                }
#endif /* CYASSL_CERT_GEN */
            }

            if (copy) {
                XMEMCPY(&full[idx], &cert->source[cert->srcIdx], strLen);
                idx += strLen;
            }

            ShaUpdate(&sha, &cert->source[cert->srcIdx], strLen);
            cert->srcIdx += strLen;
        }
        else {
            /* skip */
            byte email = FALSE;
            int  adv;

            if (joint[0] == 0x2a && joint[1] == 0x86)  /* email id hdr */
                email = TRUE;

            cert->srcIdx += oidSz + 1;

            if (GetLength(cert->source, &cert->srcIdx, &adv) < 0)
                return ASN_PARSE_E;

            if (adv > (int)(ASN_NAME_MAX - idx))
                return ASN_PARSE_E; 

            if (email) {
                if (14 > (ASN_NAME_MAX - idx))
                    return ASN_PARSE_E; 
                XMEMCPY(&full[idx], "/emailAddress=", 14);
                idx += 14;

#ifdef CYASSL_CERT_GEN
                if (nameType == SUBJECT) {
                    cert->subjectEmail = (char*)&cert->source[cert->srcIdx];
                    cert->subjectEmailLen = adv;
                }
#endif /* CYASSL_CERT_GEN */

                XMEMCPY(&full[idx], &cert->source[cert->srcIdx], adv);
                idx += adv;
            }

            cert->srcIdx += adv;
        }
    }
    full[idx++] = 0;

    if (nameType == ISSUER)
        ShaFinal(&sha, cert->issuerHash);
    else
        ShaFinal(&sha, cert->subjectHash);

    return 0;
}


#ifndef NO_TIME_H

/* to the second */
static int DateGreaterThan(const struct tm* a, const struct tm* b)
{
    if (a->tm_year > b->tm_year)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon > b->tm_mon)
        return 1;
    
    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
           a->tm_mday > b->tm_mday)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour > b->tm_hour)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour == b->tm_hour &&
        a->tm_min > b->tm_min)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour == b->tm_hour &&
        a->tm_min  == b->tm_min  && a->tm_sec > b->tm_sec)
        return 1;

    return 0; /* false */
}


static INLINE int DateLessThan(const struct tm* a, const struct tm* b)
{
    return !DateGreaterThan(a,b);
}


/* like atoi but only use first byte */
/* Make sure before and after dates are valid */
static int ValidateDate(const byte* date, byte format, int dateType)
{
    time_t ltime;
    struct tm  certTime;
    struct tm* localTime;
    int    i = 0;

    ltime = XTIME(0);
    XMEMSET(&certTime, 0, sizeof(certTime));

    if (format == ASN_UTC_TIME) {
        if (btoi(date[0]) >= 5)
            certTime.tm_year = 1900;
        else
            certTime.tm_year = 2000;
    }
    else  { /* format == GENERALIZED_TIME */
        certTime.tm_year += btoi(date[i++]) * 1000;
        certTime.tm_year += btoi(date[i++]) * 100;
    }

    GetTime(&certTime.tm_year, date, &i); certTime.tm_year -= 1900; /* adjust */
    GetTime(&certTime.tm_mon,  date, &i); certTime.tm_mon  -= 1;    /* adjust */
    GetTime(&certTime.tm_mday, date, &i);
    GetTime(&certTime.tm_hour, date, &i); 
    GetTime(&certTime.tm_min,  date, &i); 
    GetTime(&certTime.tm_sec,  date, &i); 

    if (date[i] != 'Z')     /* only Zulu supported for this profile */
        return 0;

    localTime = XGMTIME(&ltime);

    if (dateType == BEFORE) {
        if (DateLessThan(localTime, &certTime))
            return 0;
    }
    else
        if (DateGreaterThan(localTime, &certTime))
            return 0;

    return 1;
}

#endif /* NO_TIME_H */


static int GetDate(DecodedCert* cert, int dateType)
{
    int    length;
    byte   date[MAX_DATE_SIZE];
    byte   b = cert->source[cert->srcIdx++];

    if (b != ASN_UTC_TIME && b != ASN_GENERALIZED_TIME)
        return ASN_TIME_E;

    if (GetLength(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;

    if (length > MAX_DATE_SIZE || length < MIN_DATE_SIZE)
        return ASN_DATE_SZ_E;

    XMEMCPY(date, &cert->source[cert->srcIdx], length);
    cert->srcIdx += length;

    if (!XVALIDATE_DATE(date, b, dateType)) {
        if (dateType == BEFORE)
            return ASN_BEFORE_DATE_E;
        else
            return ASN_AFTER_DATE_E;
    }

    return 0;
}


static int GetValidity(DecodedCert* cert, int verify)
{
    int length;
    int badDate = 0;

    if (GetSequence(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;

    if (GetDate(cert, BEFORE) < 0 && verify)
        badDate = ASN_BEFORE_DATE_E;           /* continue parsing */
    
    if (GetDate(cert, AFTER) < 0 && verify)
        return ASN_AFTER_DATE_E;
   
    if (badDate != 0)
        return badDate;

    return 0;
}


static int DecodeToKey(DecodedCert* cert, word32 inSz, int verify)
{
    int badDate = 0;
    int ret;

    if ( (ret = GetCertHeader(cert, inSz)) < 0)
        return ret;

    if ( (ret = GetAlgoId(cert->source, &cert->srcIdx,&cert->signatureOID)) < 0)
        return ret;

    if ( (ret = GetName(cert, ISSUER)) < 0)
        return ret;

    if ( (ret = GetValidity(cert, verify)) < 0)
        badDate = ret;

    if ( (ret = GetName(cert, SUBJECT)) < 0)
        return ret;

    if ( (ret = GetKey(cert)) < 0)
        return ret;

    if (badDate != 0)
        return badDate;

    return ret;
}


static int GetSignature(DecodedCert* cert)
{
    int    length;
    byte   b = cert->source[cert->srcIdx++];

    if (b != ASN_BIT_STRING)
        return ASN_BITSTR_E;

    if (GetLength(cert->source, &cert->srcIdx, &length) < 0)
        return ASN_PARSE_E;

    cert->sigLength = length;

    b = cert->source[cert->srcIdx++];
    if (b != 0x00)
        return ASN_EXPECT_0_E;

    cert->sigLength--;
    cert->signature = &cert->source[cert->srcIdx];
    cert->srcIdx += cert->sigLength;

    return 0;
}


static word32 SetDigest(const byte* digest, word32 digSz, byte* output)
{
    output[0] = ASN_OCTET_STRING;
    output[1] = digSz;
    XMEMCPY(&output[2], digest, digSz);

    return digSz + 2;
} 


static word32 BytePrecision(word32 value)
{
    word32 i;
    for (i = sizeof(value); i; --i)
        if (value >> (i - 1) * 8)
            break;

    return i;
}


static word32 SetLength(word32 length, byte* output)
{
    word32 i = 0, j;

    if (length < ASN_LONG_LENGTH)
        output[i++] = length;
    else {
        output[i++] = BytePrecision(length) | ASN_LONG_LENGTH;
      
        for (j = BytePrecision(length); j; --j) {
            output[i] = length >> (j - 1) * 8;
            i++;
        }
    }

    return i;
}


static word32 SetSequence(word32 len, byte* output)
{
    output[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
    return SetLength(len, output + 1) + 1;
}


static word32 SetAlgoID(int algoOID, byte* output, int type)
{
    /* adding TAG_NULL and 0 to end */
    
    /* hashTypes */
    static const byte shaAlgoID[] = { 0x2b, 0x0e, 0x03, 0x02, 0x1a,
                                      0x05, 0x00 };
    static const byte md5AlgoID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                      0x02, 0x05, 0x05, 0x00  };
    static const byte md2AlgoID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                      0x02, 0x02, 0x05, 0x00};

    /* sigTypes */
    static const byte md5wRSA_AlgoID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                      0x01, 0x01, 0x04, 0x05, 0x00};

    /* keyTypes */
    static const byte RSA_AlgoID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                      0x01, 0x01, 0x01, 0x05, 0x00};

    int    algoSz = 0;
    word32 idSz, seqSz;
    const  byte* algoName = 0;
    byte ID_Length[MAX_LENGTH_SZ];
    byte seqArray[MAX_SEQ_SZ + 1];  /* add object_id to end */

    if (type == hashType) {
        switch (algoOID) {
        case SHAh:
            algoSz = sizeof(shaAlgoID);
            algoName = shaAlgoID;
            break;

        case MD2h:
            algoSz = sizeof(md2AlgoID);
            algoName = md2AlgoID;
            break;

        case MD5h:
            algoSz = sizeof(md5AlgoID);
            algoName = md5AlgoID;
            break;

        default:
            return 0;  /* UNKOWN_HASH_E; */
        }
    }
    else if (type == sigType) {    /* sigType */
        switch (algoOID) {
        case MD5wRSA:
            algoSz = sizeof(md5wRSA_AlgoID);
            algoName = md5wRSA_AlgoID;
            break;

        default:
            return 0;  /* UNKOWN_HASH_E; */
        }
    }
    else if (type == keyType) {    /* keyType */
        switch (algoOID) {
        case RSAk:
            algoSz = sizeof(RSA_AlgoID);
            algoName = RSA_AlgoID;
            break;

        default:
            return 0;  /* UNKOWN_HASH_E; */
        }
    }
    else
        return 0;  /* UNKNOWN_TYPE */


    idSz  = SetLength(algoSz - 2, ID_Length); /* don't include TAG_NULL/0 */
    seqSz = SetSequence(idSz + algoSz + 1, seqArray);
    seqArray[seqSz++] = ASN_OBJECT_ID;

    XMEMCPY(output, seqArray, seqSz);
    XMEMCPY(output + seqSz, ID_Length, idSz);
    XMEMCPY(output + seqSz + idSz, algoName, algoSz);

    return seqSz + idSz + algoSz;

}


word32 EncodeSignature(byte* out, const byte* digest, word32 digSz, int hashOID)
{
    byte digArray[MAX_ENCODED_DIG_SZ];
    byte algoArray[MAX_ALGO_SZ];
    byte seqArray[MAX_SEQ_SZ];
    word32 encDigSz, algoSz, seqSz; 

    encDigSz = SetDigest(digest, digSz, digArray);
    algoSz   = SetAlgoID(hashOID, algoArray, hashType);
    seqSz    = SetSequence(encDigSz + algoSz, seqArray);

    XMEMCPY(out, seqArray, seqSz);
    XMEMCPY(out + seqSz, algoArray, algoSz);
    XMEMCPY(out + seqSz + algoSz, digArray, encDigSz);

    return encDigSz + algoSz + seqSz;
}
                           

/* return true (1) for Confirmation */
static int ConfirmSignature(DecodedCert* cert, const byte* key, word32 keySz,
                            word32 keyOID)
{
    byte digest[SHA_DIGEST_SIZE]; /* max size */
    int  hashType, digestSz, ret;

    if (cert->signatureOID == MD5wRSA) {
        Md5 md5;
        InitMd5(&md5);
        Md5Update(&md5, cert->source + cert->certBegin,
                  cert->sigIndex - cert->certBegin);
        Md5Final(&md5, digest);
        hashType = MD5h;
        digestSz = MD5_DIGEST_SIZE;
    }
    else if (cert->signatureOID == SHAwRSA || cert->signatureOID == SHAwDSA) {
        Sha sha;
        InitSha(&sha);
        ShaUpdate(&sha, cert->source + cert->certBegin,
                  cert->sigIndex - cert->certBegin);
        ShaFinal(&sha, digest);
        hashType = SHAh;
        digestSz = SHA_DIGEST_SIZE;
    }
    else
        return 0; /* ASN_SIG_HASH_E; */

    if (keyOID == RSAk) {
        RsaKey pubKey;
        byte   encodedSig[MAX_ENCODED_SIG_SZ];
        byte   plain[MAX_ENCODED_SIG_SZ];
        word32 idx = 0;
        int    sigSz, verifySz;
        byte*  out;

        if (cert->sigLength > MAX_ENCODED_SIG_SZ)
            return 0; /* the key is too big */
            
        InitRsaKey(&pubKey, cert->heap);
        if (RsaPublicKeyDecode(key, &idx, &pubKey, keySz) < 0)
            ret = 0; /* ASN_KEY_DECODE_E; */

        else {
            XMEMCPY(plain, cert->signature, cert->sigLength);
            if ( (verifySz = RsaSSL_VerifyInline(plain, cert->sigLength, &out,
                                           &pubKey)) < 0)
                ret = 0; /* ASN_VERIFY_E; */
            else {
                /* make sure we're right justified */
                sigSz = EncodeSignature(encodedSig, digest, digestSz, hashType);
                if (sigSz != verifySz || XMEMCMP(out, encodedSig, sigSz) != 0)
                    ret = 0; /* ASN_VERIFY_MATCH_E; */
                else
                    ret = 1; /* match */
            }
        }
        FreeRsaKey(&pubKey);
        return ret;
    }
    else
        return 0; /* ASN_SIG_KEY_E; */
}


int ParseCert(DecodedCert* cert, word32 inSz, int type, int verify,
              Signer* signers)
{
    int   ret;
    char* ptr;

    ret = ParseCertRelative(cert, inSz, type, verify, signers);
    if (ret < 0)
        return ret;

    if (cert->subjectCNLen > 0) {
        ptr = (char*) XMALLOC(cert->subjectCNLen + 1, cert->heap,
                              DYNAMIC_TYPE_SUBJECT_CN);
        if (ptr == NULL)
            return MEMORY_E;
        XMEMCPY(ptr, cert->subjectCN, cert->subjectCNLen);
        ptr[cert->subjectCNLen] = '\0';
        cert->subjectCN = ptr;
        cert->subjectCNLen = 0;
    }

    if (cert->keyOID == RSAk && cert->pubKeySize > 0) {
        ptr = (char*) XMALLOC(cert->pubKeySize, cert->heap,
                              DYNAMIC_TYPE_PUBLIC_KEY);
        if (ptr == NULL)
            return MEMORY_E;
        XMEMCPY(ptr, cert->publicKey, cert->pubKeySize);
        cert->publicKey = (byte *)ptr;
        cert->pubKeyStored = 1;
    }

    return ret;
}


int ParseCertRelative(DecodedCert* cert, word32 inSz, int type, int verify,
              Signer* signers)
{
    word32 confirmOID;
    int    ret;
    int    badDate = 0;
    int    confirm = 0;

    if ((ret = DecodeToKey(cert, inSz, verify)) < 0) {
        if (ret == ASN_BEFORE_DATE_E || ret == ASN_AFTER_DATE_E)
            badDate = ret;
        else
            return ret;
    }

    if (cert->srcIdx != cert->sigIndex)
        cert->srcIdx =  cert->sigIndex;

    if ((ret = GetAlgoId(cert->source, &cert->srcIdx, &confirmOID)) < 0)
        return ret;

    if ((ret = GetSignature(cert)) < 0)
        return ret;

    if (confirmOID != cert->signatureOID)
        return ASN_SIG_OID_E;

    if (verify && type != CA_TYPE) {
        while (signers) {
            if (XMEMCMP(cert->issuerHash, signers->hash, SHA_DIGEST_SIZE)
                       == 0) {
                /* other confirm */
                if (!ConfirmSignature(cert, signers->publicKey,
                                      signers->pubKeySize, signers->keyOID))
                    return ASN_SIG_CONFIRM_E;
                else {
                    confirm = 1;
                    break;
                }
            }
            signers = signers->next;
        }
        if (!confirm)
            return ASN_SIG_CONFIRM_E;
    }
    if (badDate != 0)
        return badDate;

    return 0;
}


Signer* MakeSigner(void* heap)
{
    Signer* signer = (Signer*) XMALLOC(sizeof(Signer), heap,
                                       DYNAMIC_TYPE_SIGNER);
    if (signer) {
        signer->name      = 0;
        signer->publicKey = 0;
        signer->next      = 0;
    }

    return signer;
}


void FreeSigners(Signer* signer, void* heap)
{
    Signer* next = signer;

    while( (signer = next) ) {
        next = signer->next;
        XFREE(signer->name, heap, DYNAMIC_TYPE_SUBJECT_CN);
        XFREE(signer->publicKey, heap, DYNAMIC_TYPE_PUBLIC_KEY);
        XFREE(signer, heap, DYNAMIC_TYPE_SIGNER);
    }
}


void CTaoCryptErrorString(int error, char* buffer)
{
    const int max = MAX_ERROR_SZ;   /* shorthand */

#ifdef NO_ERROR_STRINGS

    XSTRNCPY(buffer, "no support for error strings built in", max);

#else

    switch (error) {

    case OPEN_RAN_E :        
        XSTRNCPY(buffer, "opening random device error", max);
        break;

    case READ_RAN_E :
        XSTRNCPY(buffer, "reading random device error", max);
        break;

    case WINCRYPT_E :
        XSTRNCPY(buffer, "windows crypt init error", max);
        break;

    case CRYPTGEN_E : 
        XSTRNCPY(buffer, "windows crypt generation error", max);
        break;

    case RAN_BLOCK_E : 
        XSTRNCPY(buffer, "random device read would block error", max);
        break;

    case MP_INIT_E :
        XSTRNCPY(buffer, "mp_init error state", max);
        break;

    case MP_READ_E :
        XSTRNCPY(buffer, "mp_read error state", max);
        break;

    case MP_EXPTMOD_E :
        XSTRNCPY(buffer, "mp_exptmod error state", max);
        break;

    case MP_TO_E :
        XSTRNCPY(buffer, "mp_to_xxx error state, can't convert", max);
        break;

    case MP_SUB_E :
        XSTRNCPY(buffer, "mp_sub error state, can't subtract", max);
        break;

    case MP_ADD_E :
        XSTRNCPY(buffer, "mp_add error state, can't add", max);
        break;

    case MP_MUL_E :
        XSTRNCPY(buffer, "mp_mul error state, can't multiply", max);
        break;

    case MP_MULMOD_E :
        XSTRNCPY(buffer, "mp_mulmod error state, can't multiply mod", max);
        break;

    case MP_MOD_E :
        XSTRNCPY(buffer, "mp_mod error state, can't mod", max);
        break;

    case MP_INVMOD_E :
        XSTRNCPY(buffer, "mp_invmod error state, can't inv mod", max);
        break; 
        
    case MP_CMP_E :
        XSTRNCPY(buffer, "mp_cmp error state", max);
        break; 
        
    case MEMORY_E :
        XSTRNCPY(buffer, "out of memory error", max);
        break;

    case RSA_WRONG_TYPE_E :
        XSTRNCPY(buffer, "RSA wrong block type for RSA function", max);
        break; 

    case RSA_BUFFER_E :
        XSTRNCPY(buffer, "RSA buffer error, output too small or input too big",
                max);
        break; 

    case BUFFER_E :
        XSTRNCPY(buffer, "Buffer error, output too small or input too big", max);
        break; 

    case ALGO_ID_E :
        XSTRNCPY(buffer, "Setting Cert AlogID error", max);
        break; 

    case PUBLIC_KEY_E :
        XSTRNCPY(buffer, "Setting Cert Public Key error", max);
        break; 

    case DATE_E :
        XSTRNCPY(buffer, "Setting Cert Date validity error", max);
        break; 

    case SUBJECT_E :
        XSTRNCPY(buffer, "Setting Cert Subject name error", max);
        break; 

    case ISSUER_E :
        XSTRNCPY(buffer, "Setting Cert Issuer name error", max);
        break; 

    case ASN_PARSE_E :
        XSTRNCPY(buffer, "ASN parsing error, invalid input", max);
        break;

    case ASN_VERSION_E :
        XSTRNCPY(buffer, "ASN version error, invalid number", max);
        break;

    case ASN_GETINT_E :
        XSTRNCPY(buffer, "ASN get big int error, invalid data", max);
        break;

    case ASN_RSA_KEY_E :
        XSTRNCPY(buffer, "ASN key init error, invalid input", max);
        break;

    case ASN_OBJECT_ID_E :
        XSTRNCPY(buffer, "ASN object id error, invalid id", max);
        break;

    case ASN_TAG_NULL_E :
        XSTRNCPY(buffer, "ASN tag error, not null", max);
        break;

    case ASN_EXPECT_0_E :
        XSTRNCPY(buffer, "ASN expect error, not zero", max);
        break;

    case ASN_BITSTR_E :
        XSTRNCPY(buffer, "ASN bit string error, wrong id", max);
        break;

    case ASN_UNKNOWN_OID_E :
        XSTRNCPY(buffer, "ASN oid error, unknown sum id", max);
        break;

    case ASN_DATE_SZ_E :
        XSTRNCPY(buffer, "ASN date error, bad size", max);
        break;

    case ASN_BEFORE_DATE_E :
        XSTRNCPY(buffer, "ASN date error, current date before", max);
        break;

    case ASN_AFTER_DATE_E :
        XSTRNCPY(buffer, "ASN date error, current date after", max);
        break;

    case ASN_SIG_OID_E :
        XSTRNCPY(buffer, "ASN signature error, mismatched oid", max);
        break;

    case ASN_TIME_E :
        XSTRNCPY(buffer, "ASN time error, unkown time type", max);
        break;

    case ASN_INPUT_E :
        XSTRNCPY(buffer, "ASN input error, not enough data", max);
        break;

    case ASN_SIG_CONFIRM_E :
        XSTRNCPY(buffer, "ASN sig error, confirm failure", max);
        break;

    case ASN_SIG_HASH_E :
        XSTRNCPY(buffer, "ASN sig error, unsupported hash type", max);
        break;

    case ASN_SIG_KEY_E :
        XSTRNCPY(buffer, "ASN sig error, unsupported key type", max);
        break;

    case ASN_DH_KEY_E :
        XSTRNCPY(buffer, "ASN key init error, invalid input", max);
        break;

    case ASN_NTRU_KEY_E :
        XSTRNCPY(buffer, "ASN NTRU key decode error, invalid input", max);
        break;

    default:
        XSTRNCPY(buffer, "unknown error number", max);

    }

#endif /* NO_ERROR_STRINGS */

}


#if defined(CYASSL_KEY_GEN) || defined(CYASSL_CERT_GEN)

static int SetMyVersion(word32 version, byte* output, int header)
{
    int i = 0;

    if (header) {
        output[i++] = ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED;
        output[i++] = ASN_BIT_STRING;
    }
    output[i++] = ASN_INTEGER;
    output[i++] = 0x01;
    output[i++] = version;

    return i;
}


int DerToPem(const byte* der, word32 derSz, byte* output, word32 outSz,
             int type)
{
    char header[80];
    char footer[80];

    int headerLen;
    int footerLen;
    int i;
    int outLen;   /* return length or error */

    if (type == CERT_TYPE) {
        XSTRNCPY(header, "-----BEGIN CERTIFICATE-----\n", sizeof(header));
        XSTRNCPY(footer, "-----END CERTIFICATE-----\n", sizeof(footer));
    } else {
        XSTRNCPY(header, "-----BEGIN RSA PRIVATE KEY-----\n", sizeof(header));
        XSTRNCPY(footer, "-----END RSA PRIVATE KEY-----\n", sizeof(footer));
    }

    headerLen = XSTRLEN(header);
    footerLen = XSTRLEN(footer);

    if (!der || !output)
        return -1;

    /* don't even try if outSz too short */
    if (outSz < headerLen + footerLen + derSz)
        return -1;

    /* header */
    XMEMCPY(output, header, headerLen);
    i = headerLen;

    /* body */
    outLen = outSz;  /* input to Base64Encode */
    if (Base64Encode(der, derSz, output + i, (word32*)&outLen) < 0)
        return -1;
    i += outLen;

    /* footer */
    if ( (i + footerLen) > (int)outSz)
        return -1;
    XMEMCPY(output + i, footer, footerLen);

    return outLen + headerLen + footerLen;
}


#endif /* CYASSL_KEY_GEN || CYASSL_CERT_GEN */


#ifdef CYASSL_KEY_GEN


static mp_int* GetRsaInt(RsaKey* key, int index)
{
    if (index == 0)
        return &key->n;
    if (index == 1)
        return &key->e;
    if (index == 2)
        return &key->d;
    if (index == 3)
        return &key->p;
    if (index == 4)
        return &key->q;
    if (index == 5)
        return &key->dP;
    if (index == 6)
        return &key->dQ;
    if (index == 7)
        return &key->u;

    return NULL;
}


/* Convert RsaKey key to DER format, write to output (inLen), return bytes
   written */
int RsaKeyToDer(RsaKey* key, byte* output, word32 inLen)
{
    word32 seqSz, verSz, rawLen, intTotalLen = 0;
    word32 sizes[RSA_INTS];
    int    i, j, outLen;

    byte seq[MAX_SEQ_SZ];
    byte ver[MAX_VERSION_SZ];
    byte tmps[RSA_INTS][MAX_RSA_INT_SZ];

    if (!key || !output)
        return -1;

    if (key->type != RSA_PRIVATE)
        return -1;

    /* write all big ints from key to DER tmps */
    for (i = 0; i < RSA_INTS; i++) {
        mp_int* keyInt = GetRsaInt(key, i);
        rawLen = mp_unsigned_bin_size(keyInt);

        tmps[i][0] = ASN_INTEGER;
        sizes[i] = SetLength(rawLen, tmps[i] + 1) + 1;  /* int tag */

        if ( (sizes[i] + rawLen) < sizeof(tmps[i])) {
            int err = mp_to_unsigned_bin(keyInt, tmps[i] + sizes[i]);
            if (err == MP_OKAY) {
                sizes[i] += rawLen;
                intTotalLen += sizes[i];
            }
            else
                return err;
        }
        else
            return -1;
    }

    /* make headers */
    verSz = SetMyVersion(0, ver, FALSE);
    seqSz = SetSequence(verSz + intTotalLen, seq);

    outLen = seqSz + verSz + intTotalLen;
    if (outLen > (int)inLen)
        return -1;

    /* write to output */
    XMEMCPY(output, seq, seqSz);
    j = seqSz;
    XMEMCPY(output + j, ver, verSz);
    j += verSz;

    for (i = 0; i < RSA_INTS; i++) {
        XMEMCPY(output + j, tmps[i], sizes[i]);
        j += sizes[i];
    }

    return outLen;
}

#endif /* CYASSL_KEY_GEN */


#ifdef CYASSL_CERT_GEN

/* Initialize and Set Certficate defaults:
   version    = 3 (0x2)
   serial     = 0
   sigType    = MD5_WITH_RSA
   issuer     = blank
   daysValid  = 500
   selfSigned = 1 (true) use subject as issuer
   subject    = blank
*/
void InitCert(Cert* cert)
{
    cert->version    = 2;   /* version 3 is hex 2 */
    cert->sigType    = MD5wRSA;
    cert->daysValid  = 500;
    cert->selfSigned = 1;
    cert->bodySz     = 0;
    cert->keyType    = RSA_KEY;
    XMEMSET(cert->serial, 0, SERIAL_SIZE);

    cert->issuer.country[0] = '\0';
    cert->issuer.state[0] = '\0';
    cert->issuer.locality[0] = '\0';
    cert->issuer.sur[0] = '\0';
    cert->issuer.org[0] = '\0';
    cert->issuer.unit[0] = '\0';
    cert->issuer.commonName[0] = '\0';
    cert->issuer.email[0] = '\0';

    cert->subject.country[0] = '\0';
    cert->subject.state[0] = '\0';
    cert->subject.locality[0] = '\0';
    cert->subject.sur[0] = '\0';
    cert->subject.org[0] = '\0';
    cert->subject.unit[0] = '\0';
    cert->subject.commonName[0] = '\0';
    cert->subject.email[0] = '\0';
}


/* DER encoded x509 Certificate */
typedef struct DerCert {
    byte size[MAX_LENGTH_SZ];          /* length encoded */
    byte version[MAX_VERSION_SZ];      /* version encoded */
    byte serial[SERIAL_SIZE + MAX_LENGTH_SZ]; /* serial number encoded */
    byte sigAlgo[MAX_ALGO_SZ];         /* signature algo encoded */
    byte issuer[ASN_NAME_MAX];         /* issuer  encoded */
    byte subject[ASN_NAME_MAX];        /* subject encoded */
    byte validity[MAX_DATE_SIZE*2 + MAX_SEQ_SZ*2];  /* before and after dates */
    byte publicKey[MAX_PUBLIC_KEY_SZ]; /* rsa / ntru public key encoded */
    int  sizeSz;                       /* encoded size length */
    int  versionSz;                    /* encoded version length */
    int  serialSz;                     /* encoded serial length */
    int  sigAlgoSz;                    /* enocded sig alog length */
    int  issuerSz;                     /* encoded issuer length */
    int  subjectSz;                    /* encoded subject length */
    int  validitySz;                   /* encoded validity length */
    int  publicKeySz;                  /* encoded public key length */
    int  total;                        /* total encoded lengths */
} DerCert;


/* Write a set header to output */
static word32 SetSet(word32 len, byte* output)
{
    output[0] = ASN_SET | ASN_CONSTRUCTED;
    return SetLength(len, output + 1) + 1;
}


/* Write a serial number to output */
static int SetSerial(const byte* serial, byte* output)
{
    int length = 0;

    output[length++] = ASN_INTEGER;
    length += SetLength(SERIAL_SIZE, &output[length]);
    XMEMCPY(&output[length], serial, SERIAL_SIZE);

    return length + SERIAL_SIZE;
}


/* Write a public RSA key to output */
static int SetPublicKey(byte* output, RsaKey* key)
{
    byte n[MAX_RSA_INT_SZ];
    byte e[MAX_RSA_E_SZ];
    byte algo[MAX_ALGO_SZ];
    byte seq[MAX_SEQ_SZ];
    byte len[MAX_LENGTH_SZ + 1];  /* trailing 0 */
    int  nSz;
    int  eSz;
    int  algoSz;
    int  seqSz;
    int  lenSz;
    int  idx;
    int  rawLen;

    /* n */
    rawLen = mp_unsigned_bin_size(&key->n);
    n[0] = ASN_INTEGER;
    nSz  = SetLength(rawLen, n + 1) + 1;  /* int tag */

    if ( (nSz + rawLen) < sizeof(n)) {
        int err = mp_to_unsigned_bin(&key->n, n + nSz);
        if (err == MP_OKAY)
            nSz += rawLen;
        else
            return MP_TO_E;
    }
    else
        return BUFFER_E;

    /* e */
    rawLen = mp_unsigned_bin_size(&key->e);
    e[0] = ASN_INTEGER;
    eSz  = SetLength(rawLen, e + 1) + 1;  /* int tag */

    if ( (eSz + rawLen) < sizeof(e)) {
        int err = mp_to_unsigned_bin(&key->e, e + eSz);
        if (err == MP_OKAY)
            eSz += rawLen;
        else
            return MP_TO_E;
    }
    else
        return BUFFER_E;

    /* headers */
    algoSz = SetAlgoID(RSAk, algo, keyType);
    seqSz  = SetSequence(nSz + eSz, seq);
    lenSz  = SetLength(seqSz + nSz + eSz + 1, len);
    len[lenSz++] = 0;   /* trailing 0 */

    /* write */
    idx = SetSequence(nSz + eSz + seqSz + lenSz + 1 + algoSz, output);
        /* 1 is for ASN_BIT_STRING */
    /* algo */
    XMEMCPY(output + idx, algo, algoSz);
    idx += algoSz;
    /* bit string */
    output[idx++] = ASN_BIT_STRING;
    /* length */
    XMEMCPY(output + idx, len, lenSz);
    idx += lenSz;
    /* seq */
    XMEMCPY(output + idx, seq, seqSz);
    idx += seqSz;
    /* n */
    XMEMCPY(output + idx, n, nSz);
    idx += nSz;
    /* e */
    XMEMCPY(output + idx, e, eSz);
    idx += eSz;

    return idx;
}


static INLINE byte itob(int number)
{
    return (byte)number + 0x30;
}


/* write time to output, format */
static void SetTime(struct tm* date, byte* output)
{
    int i = 0;

    output[i++] = itob((date->tm_year % 10000) / 1000);
    output[i++] = itob((date->tm_year % 1000)  /  100);
    output[i++] = itob((date->tm_year % 100)   /   10);
    output[i++] = itob( date->tm_year % 10);

    output[i++] = itob(date->tm_mon / 10);
    output[i++] = itob(date->tm_mon % 10);

    output[i++] = itob(date->tm_mday / 10);
    output[i++] = itob(date->tm_mday % 10);

    output[i++] = itob(date->tm_hour / 10);
    output[i++] = itob(date->tm_hour % 10);

    output[i++] = itob(date->tm_min / 10);
    output[i++] = itob(date->tm_min % 10);

    output[i++] = itob(date->tm_sec / 10);
    output[i++] = itob(date->tm_sec % 10);
    
    output[i] = 'Z';  /* Zulu profiel */
}


/* Set Date validity from now until now + daysValid */
static int SetValidity(byte* output, int daysValid)
{
    byte before[MAX_DATE_SIZE];
    byte  after[MAX_DATE_SIZE];

    int beforeSz;
    int afterSz;
    int seqSz;

    time_t     ticks;
    struct tm* now;
    struct tm  local;

    ticks = XTIME(0);
    now   = XGMTIME(&ticks);

    /* before now */
    local = *now;
    before[0] = ASN_GENERALIZED_TIME;
    beforeSz  = SetLength(ASN_GEN_TIME_SZ, before + 1) + 1;  /* gen tag */

    /* adjust */
    local.tm_year += 1900;
    local.tm_mon  +=    1;

    SetTime(&local, before + beforeSz);
    beforeSz += ASN_GEN_TIME_SZ;
    
    /* after now + daysValid */
    local = *now;
    after[0] = ASN_GENERALIZED_TIME;
    afterSz  = SetLength(ASN_GEN_TIME_SZ, after + 1) + 1;  /* gen tag */

    /* add daysValid */
    local.tm_mday += daysValid;
    mktime(&local);

    /* adjust */
    local.tm_year += 1900;
    local.tm_mon  +=    1;

    SetTime(&local, after + afterSz);
    afterSz += ASN_GEN_TIME_SZ;

    /* headers and output */
    seqSz = SetSequence(beforeSz + afterSz, output);
    XMEMCPY(output + seqSz, before, beforeSz);
    XMEMCPY(output + seqSz + beforeSz, after, afterSz);

    return seqSz + beforeSz + afterSz;
}


/* ASN Encoded Name field */
typedef struct EncodedName {
    int  nameLen;                /* actual string value length */
    int  totalLen;               /* total encodeding length */
    int  type;                   /* type of name */
    int  used;                   /* are we actually using this one */
    byte encoded[NAME_SIZE * 2]; /* encoding */
} EncodedName;


/* Get Which Name from index */
static const char* GetOneName(CertName* name, int index)
{
    switch (index) {
    case 0:
       return name->country;
       break;
    case 1:
       return name->state;
       break;
    case 2:
       return name->locality;
       break;
    case 3:
       return name->sur;
       break;
    case 4:
       return name->org;
       break;
    case 5:
       return name->unit;
       break;
    case 6:
       return name->commonName;
       break;
    case 7:
       return name->email;
       break;
    default:
       return 0;
    }

    return 0;
}


/* Get ASN Name from index */
static byte GetNameId(int index)
{
    switch (index) {
    case 0:
       return ASN_COUNTRY_NAME;
       break;
    case 1:
       return ASN_STATE_NAME;
       break;
    case 2:
       return ASN_LOCALITY_NAME;
       break;
    case 3:
       return ASN_SUR_NAME;
       break;
    case 4:
       return ASN_ORG_NAME;
       break;
    case 5:
       return ASN_ORGUNIT_NAME;
       break;
    case 6:
       return ASN_COMMON_NAME;
       break;
    case 7:
       /* email uses different id type */
       return 0;
       break;
    default:
       return 0;
    }

    return 0;
}


/* encode CertName into output, return total bytes written */
static int SetName(byte* output, CertName* name)
{
    int         totalBytes = 0, i, idx;
    EncodedName names[NAME_ENTRIES];

    for (i = 0; i < NAME_ENTRIES; i++) {
        const char* nameStr = GetOneName(name, i);
        if (nameStr) {
            /* bottom up */
            byte firstLen[MAX_LENGTH_SZ];
            byte secondLen[MAX_LENGTH_SZ];
            byte sequence[MAX_SEQ_SZ];
            byte set[MAX_SET_SZ];

            int email = i == (NAME_ENTRIES - 1) ? 1 : 0;
            int strLen  = XSTRLEN(nameStr);
            int thisLen = strLen;
            int firstSz, secondSz, seqSz, setSz;

            if (strLen == 0) { /* no user data for this item */
                names[i].used = 0;
                continue;
            }

            secondSz = SetLength(strLen, secondLen);
            thisLen += secondSz;
            if (email) {
                thisLen += EMAIL_JOINT_LEN;
                thisLen ++;                               /* id type */
                firstSz  = SetLength(EMAIL_JOINT_LEN, firstLen);
            }
            else {
                thisLen++;                                 /* str type */
                thisLen++;                                 /* id  type */
                thisLen += JOINT_LEN;    
                firstSz = SetLength(JOINT_LEN + 1, firstLen);
            }
            thisLen += firstSz;
            thisLen++;                                /* object id */

            seqSz = SetSequence(thisLen, sequence);
            thisLen += seqSz;
            setSz = SetSet(thisLen, set);
            thisLen += setSz;

            if (thisLen > sizeof(names[i].encoded))
                return BUFFER_E;

            /* store it */
            idx = 0;
            /* set */
            XMEMCPY(names[i].encoded, set, setSz);
            idx += setSz;
            /* seq */
            XMEMCPY(names[i].encoded + idx, sequence, seqSz);
            idx += seqSz;
            /* asn object id */
            names[i].encoded[idx++] = ASN_OBJECT_ID;
            /* first length */
            XMEMCPY(names[i].encoded + idx, firstLen, firstSz);
            idx += firstSz;
            if (email) {
                const byte EMAIL_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                           0x01, 0x09, 0x01, 0x16 };
                /* email joint id */
                XMEMCPY(names[i].encoded + idx, EMAIL_OID, sizeof(EMAIL_OID));
                idx += sizeof(EMAIL_OID);
            }
            else {
                /* joint id */
                names[i].encoded[idx++] = 0x55;
                names[i].encoded[idx++] = 0x04;
                /* id type */
                names[i].encoded[idx++] = GetNameId(i);
                /* str type */
                names[i].encoded[idx++] = 0x13;
            }
            /* second length */
            XMEMCPY(names[i].encoded + idx, secondLen, secondSz);
            idx += secondSz;
            /* str value */
            XMEMCPY(names[i].encoded + idx, nameStr, strLen);
            idx += strLen;

            totalBytes += idx;
            names[i].totalLen = idx;
            names[i].used = 1;
        }
        else
            names[i].used = 0;
    }

    /* header */
    idx = SetSequence(totalBytes, output);
    totalBytes += idx;
    if (totalBytes > ASN_NAME_MAX)
        return BUFFER_E;

    for (i = 0; i < NAME_ENTRIES; i++) {
        if (names[i].used) {
            XMEMCPY(output + idx, names[i].encoded, names[i].totalLen);
            idx += names[i].totalLen;
        }
    }
    return totalBytes;
}


/* encode info from cert into DER enocder format */
static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, RNG* rng,
                      const byte* ntruKey, word16 ntruSz)
{
    /* version */
    der->versionSz = SetMyVersion(cert->version, der->version, TRUE);

    /* serial number */
    RNG_GenerateBlock(rng, cert->serial, SERIAL_SIZE);
    cert->serial[0] = 0x01;   /* ensure positive */
    der->serialSz  = SetSerial(cert->serial, der->serial);

    /* signature algo */
    der->sigAlgoSz = SetAlgoID(cert->sigType, der->sigAlgo, sigType);
    if (der->sigAlgoSz == 0)
        return ALGO_ID_E;

    /* public key */
    if (cert->keyType == RSA_KEY) {
        der->publicKeySz = SetPublicKey(der->publicKey, rsaKey);
        if (der->publicKeySz == 0)
            return PUBLIC_KEY_E;
    }
    else {
#ifdef HAVE_NTRU
        word32 rc;
        word16 encodedSz;

        rc  = crypto_ntru_encrypt_publicKey2SubjectPublicKeyInfo( ntruSz,
                                              ntruKey, &encodedSz, NULL);
        if (rc != NTRU_OK)
            return PUBLIC_KEY_E;
        if (encodedSz > MAX_PUBLIC_KEY_SZ)
            return PUBLIC_KEY_E;

        rc  = crypto_ntru_encrypt_publicKey2SubjectPublicKeyInfo( ntruSz,
                              ntruKey, &encodedSz, der->publicKey);
        if (rc != NTRU_OK)
            return PUBLIC_KEY_E;

        der->publicKeySz = encodedSz;
#endif
    }

    /* date validity */
    der->validitySz = SetValidity(der->validity, cert->daysValid);
    if (der->validitySz == 0)
        return DATE_E;

    /* subject name */
    der->subjectSz = SetName(der->subject, &cert->subject);
    if (der->subjectSz == 0)
        return SUBJECT_E;

    /* issuer name */
    der->issuerSz = SetName(der->issuer, cert->selfSigned ?
             &cert->subject : &cert->issuer);
    if (der->issuerSz == 0)
        return ISSUER_E;

    der->total = der->versionSz + der->serialSz + der->sigAlgoSz +
        der->publicKeySz + der->validitySz + der->subjectSz + der->issuerSz;

    return 0;
}


/* write DER encoded cert to buffer, size already checked */
static int WriteCertBody(DerCert* der, byte* buffer)
{
    int idx;

    /* signed part header */
    idx = SetSequence(der->total, buffer);
    /* version */
    XMEMCPY(buffer + idx, der->version, der->versionSz);
    idx += der->versionSz;
    /* serial */
    XMEMCPY(buffer + idx, der->serial, der->serialSz);
    idx += der->serialSz;
    /* sig algo */
    XMEMCPY(buffer + idx, der->sigAlgo, der->sigAlgoSz);
    idx += der->sigAlgoSz;
    /* issuer */
    XMEMCPY(buffer + idx, der->issuer, der->issuerSz);
    idx += der->issuerSz;
    /* validity */
    XMEMCPY(buffer + idx, der->validity, der->validitySz);
    idx += der->validitySz;
    /* subject */
    XMEMCPY(buffer + idx, der->subject, der->subjectSz);
    idx += der->subjectSz;
    /* public key */
    XMEMCPY(buffer + idx, der->publicKey, der->publicKeySz);
    idx += der->publicKeySz;

    return idx;
}


/* Make MD5wRSA signature from buffer (sz), write to sig (sigSz) */
static int MakeSignature(const byte* buffer, int sz, byte* sig, int sigSz,
                         RsaKey* key, RNG* rng)
{
    byte    digest[SHA_DIGEST_SIZE];     /* max size */
    byte    encSig[MAX_ENCODED_DIG_SZ + MAX_ALGO_SZ + MAX_SEQ_SZ];
    int     encSigSz, digestSz, hashType;
    Md5     md5;                         /* md5 for now */

    InitMd5(&md5);
    Md5Update(&md5, buffer, sz);
    Md5Final(&md5, digest);
    digestSz = MD5_DIGEST_SIZE;
    hashType = MD5h;

    /* signature */
    encSigSz = EncodeSignature(encSig, digest, digestSz, hashType);
    return RsaSSL_Sign(encSig, encSigSz, sig, sigSz, key, rng);
}


/* add signature to end of buffer, size of buffer assumed checked, return
   new length */
static int AddSignature(byte* buffer, int bodySz, const byte* sig, int sigSz)
{
    byte seq[MAX_SEQ_SZ];
    int  idx = bodySz, seqSz;

    /* algo */
    idx += SetAlgoID(MD5wRSA, buffer + idx, sigType);
    /* bit string */
    buffer[idx++] = ASN_BIT_STRING;
    /* length */
    idx += SetLength(sigSz + 1, buffer + idx);
    buffer[idx++] = 0;   /* trailing 0 */
    /* signature */
    XMEMCPY(buffer + idx, sig, sigSz);
    idx += sigSz;

    /* make room for overall header */
    seqSz = SetSequence(idx, seq);
    XMEMMOVE(buffer + seqSz, buffer, idx);
    XMEMCPY(buffer, seq, seqSz);

    return idx + seqSz;
}


/* Make an x509 Certificate v3 any key type from cert input, write to buffer */
static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
                   RsaKey* rsaKey, RNG* rng, const byte* ntruKey, word16 ntruSz)
{
    DerCert der;
    int     ret;

    cert->keyType = rsaKey ? RSA_KEY : NTRU_KEY;
    ret = EncodeCert(cert, &der, rsaKey, rng, ntruKey, ntruSz);
    if (ret != 0)
        return ret;

    if (der.total + MAX_SEQ_SZ * 2 > (int)derSz)
        return BUFFER_E;

    return cert->bodySz = WriteCertBody(&der, derBuffer);
}


/* Make an x509 Certificate v3 RSA from cert input, write to buffer */
int MakeCert(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey,RNG* rng)
{
    return MakeAnyCert(cert, derBuffer, derSz, rsaKey, rng, NULL, 0);
}


#ifdef HAVE_NTRU

int  MakeNtruCert(Cert* cert, byte* derBuffer, word32 derSz,
                  const byte* ntruKey, word16 keySz, RNG* rng)
{
    return MakeAnyCert(cert, derBuffer, derSz, NULL, rng, ntruKey, keySz);
}

#endif /* HAVE_NTRU */


int SignCert(Cert* cert, byte* buffer, word32 buffSz, RsaKey* key, RNG* rng)
{
    byte    sig[MAX_ENCODED_SIG_SZ];
    int     sigSz;
    int     bodySz = cert->bodySz;

    if (bodySz < 0)
        return bodySz;

    sigSz  = MakeSignature(buffer, bodySz, sig, sizeof(sig), key, rng);
    if (sigSz < 0)
        return sigSz; 

    if (bodySz + MAX_SEQ_SZ * 2 + sigSz > (int)buffSz)
        return BUFFER_E; 

    return AddSignature(buffer, bodySz, sig, sigSz);
}


int MakeSelfCert(Cert* cert, byte* buffer, word32 buffSz, RsaKey* key, RNG* rng)
{
    int ret = MakeCert(cert, buffer, buffSz, key, rng);

    if (ret < 0)
        return ret;

    return SignCert(cert, buffer, buffSz, key, rng);
}


/* forward from CyaSSL */
int CyaSSL_PemCertToDer(const char* fileName, unsigned char* derBuf, int derSz);

#ifndef NO_FILESYSTEM

int SetIssuer(Cert* cert, const char* issuerCertFile)
{
    DecodedCert decoded;
    byte        der[8192];
    int         derSz = CyaSSL_PemCertToDer(issuerCertFile, der, sizeof(der));
    int         ret;
    int         sz;

    if (derSz < 0)
        return derSz;

    cert->selfSigned = 0;

    InitDecodedCert(&decoded, der, 0);
    ret = ParseCertRelative(&decoded, derSz, CA_TYPE, NO_VERIFY, 0);

    if (ret < 0)
        return ret;

    if (decoded.subjectCN) {
        sz = (decoded.subjectCNLen < NAME_SIZE) ? decoded.subjectCNLen :
                                                  NAME_SIZE - 1;
        strncpy(cert->issuer.commonName, decoded.subjectCN, NAME_SIZE);
        cert->issuer.commonName[sz] = 0;
    }
    if (decoded.subjectC) {
        sz = (decoded.subjectCLen < NAME_SIZE) ? decoded.subjectCLen :
                                                 NAME_SIZE - 1;
        strncpy(cert->issuer.country, decoded.subjectC, NAME_SIZE);
        cert->issuer.country[sz] = 0;
    }
    if (decoded.subjectST) {
        sz = (decoded.subjectSTLen < NAME_SIZE) ? decoded.subjectSTLen :
                                                  NAME_SIZE - 1;
        strncpy(cert->issuer.state, decoded.subjectST, NAME_SIZE);
        cert->issuer.state[sz] = 0;
    }
    if (decoded.subjectL) {
        sz = (decoded.subjectLLen < NAME_SIZE) ? decoded.subjectLLen :
                                                 NAME_SIZE - 1;
        strncpy(cert->issuer.locality, decoded.subjectL, NAME_SIZE);
        cert->issuer.locality[sz] = 0;
    }
    if (decoded.subjectO) {
        sz = (decoded.subjectOLen < NAME_SIZE) ? decoded.subjectOLen :
                                                 NAME_SIZE - 1;
        strncpy(cert->issuer.org, decoded.subjectO, NAME_SIZE);
        cert->issuer.org[sz] = 0;
    }
    if (decoded.subjectOU) {
        sz = (decoded.subjectOULen < NAME_SIZE) ? decoded.subjectOULen :
                                                  NAME_SIZE - 1;
        strncpy(cert->issuer.unit, decoded.subjectOU, NAME_SIZE);
        cert->issuer.unit[sz] = 0;
    }
    if (decoded.subjectSN) {
        sz = (decoded.subjectSNLen < NAME_SIZE) ? decoded.subjectSNLen :
                                                  NAME_SIZE - 1;
        strncpy(cert->issuer.sur, decoded.subjectSN, NAME_SIZE);
        cert->issuer.sur[sz] = 0;
    }
    if (decoded.subjectEmail) {
        sz = (decoded.subjectEmailLen < NAME_SIZE) ? decoded.subjectEmailLen :
                                                     NAME_SIZE - 1;
        strncpy(cert->issuer.email, decoded.subjectEmail, NAME_SIZE);
        cert->issuer.email[sz] = 0;
    }

    FreeDecodedCert(&decoded);

    return 0;
}

#endif /* NO_FILESYSTEM */
#endif /* CYASSL_CERT_GEN */
