CyaSSL is an SSL library for devices like mbed.

Dependents:   cyassl-client Sync

Revision:
0:5045d2638c29
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asn.c	Sat Feb 05 01:09:17 2011 +0000
@@ -0,0 +1,2648 @@
+/* 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 */