cya_u
Fork of CyaSSL-forEncrypt by
asn.c
- Committer:
- toddouska
- Date:
- 2011-02-05
- Revision:
- 0:5045d2638c29
File content as of revision 0:5045d2638c29:
/* 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(<ime); 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 */