From 9818fe4f554c01feb15a6578fee51baca9a93d1b Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 29 May 2012 09:11:37 -0700 Subject: [PATCH] changed DN hashing to cover the whole DER encoding per OCSP-RFC, OCSP changes towards dynamic storage of responses --- ctaocrypt/src/asn.c | 246 +++++++++++++++++++++++------------------ cyassl/ctaocrypt/asn.h | 47 +++++--- cyassl/internal.h | 22 ++++ cyassl/ocsp.h | 24 +--- src/ocsp.c | 11 +- 5 files changed, 198 insertions(+), 152 deletions(-) diff --git a/ctaocrypt/src/asn.c b/ctaocrypt/src/asn.c index 18d27cdad..56da47a24 100644 --- a/ctaocrypt/src/asn.c +++ b/ctaocrypt/src/asn.c @@ -1287,7 +1287,7 @@ static int GetName(DecodedCert* cert, int nameType) int length; /* length of all distinguished names */ int dummy; char* full = (nameType == ISSUER) ? cert->issuer : cert->subject; - word32 idx = 0; + word32 idx; if (cert->source[cert->srcIdx] == ASN_OBJECT_ID) { CYASSL_MSG("Trying optional prefix..."); @@ -1299,17 +1299,22 @@ static int GetName(DecodedCert* cert, int nameType) CYASSL_MSG("Got optional prefix"); } + /* For OCSP, RFC2560 section 4.1.1 states the issuer hash should be + * calculated over the entire DER encoding of the Name field, including + * the tag and length. */ + idx = cert->srcIdx; if (GetSequence(cert->source, &cert->srcIdx, &length, cert->maxIdx) < 0) return ASN_PARSE_E; InitSha(&sha); - ShaUpdate(&sha, &cert->source[cert->srcIdx], length); + ShaUpdate(&sha, &cert->source[idx], length + cert->srcIdx - idx); if (nameType == ISSUER) ShaFinal(&sha, cert->issuerHash); else ShaFinal(&sha, cert->subjectHash); length += cert->srcIdx; + idx = 0; while (cert->srcIdx < (word32)length) { byte b; @@ -4076,6 +4081,36 @@ int EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key, #endif /* HAVE_ECC */ +#if defined(HAVE_OCSP) || defined(HAVE_CRL) + +/* Get raw Date only, no processing, 0 on success */ +static int GetBasicDate(const byte* source, word32* idx, byte* date, + byte* format, int maxIdx) +{ + int length; + + CYASSL_ENTER("GetBasicDate"); + + *format = source[*idx]; + *idx += 1; + if (*format != ASN_UTC_TIME && *format != ASN_GENERALIZED_TIME) + return ASN_TIME_E; + + if (GetLength(source, idx, &length, maxIdx) < 0) + return ASN_PARSE_E; + + if (length > MAX_DATE_SIZE || length < MIN_DATE_SIZE) + return ASN_DATE_SZ_E; + + XMEMCPY(date, &source[*idx], length); + *idx += length; + + return 0; +} + +#endif + + #ifdef HAVE_OCSP static int GetEnumerated(const byte* input, word32* inOutIdx, int *value) @@ -4106,91 +4141,109 @@ static int DecodeSingleResponse(byte* source, word32* ioIndex, OcspResponse* resp, word32 size) { word32 index = *ioIndex, prevIndex, oid; - int length, remainder, qty = 0; + int length; + CertStatus* cs = NULL; /* Outer wrapper of the SEQUENCE OF Single Responses. */ if (GetSequence(source, &index, &length, size) < 0) return ASN_PARSE_E; - remainder = length; - /* First Single Response */ - while (remainder != 0 && qty < STATUS_LIST_SIZE) + /* When making a request, we only request one status on one certificate + * at a time. There should only be one SingleResponse */ + cs = resp->status;/*XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_CERT_STATUS); + if (cs == NULL) { - prevIndex = index; - /* Wrapper around the Single Response */ - if (GetSequence(source, &index, &length, size) < 0) - return ASN_PARSE_E; - - /* Wrapper around the CertID */ - if (GetSequence(source, &index, &length, size) < 0) - return ASN_PARSE_E; - /* Skip the hash algorithm */ - if (GetAlgoId(source, &index, &oid, size) < 0) - return ASN_PARSE_E; - /* Skip the hash of CN */ - if (source[index++] != ASN_OCTET_STRING) - return ASN_PARSE_E; - if (GetLength(source, &index, &length, size) < 0) - return ASN_PARSE_E; - index += length; - /* Skip the hash of the issuer public key */ - if (source[index++] != ASN_OCTET_STRING) - return ASN_PARSE_E; - if (GetLength(source, &index, &length, size) < 0) - return ASN_PARSE_E; - index += length; - - /* Read the serial number, it is handled as a string, not as a - * proper number. Just XMEMCPY the data over, rather than load it - * as an mp_int. */ - if (source[index++] != ASN_INTEGER) - return ASN_PARSE_E; - if (GetLength(source, &index, &length, size) < 0) - return ASN_PARSE_E; - if (length <= EXTERNAL_SERIAL_SIZE) { - if (source[index] == 0) { - index++; - length--; - } - XMEMCPY(resp->certSN[qty], source + index, length); - resp->certSNsz[qty] = length; - } else { - return ASN_GETINT_E; - } - index += length; - - /* CertStatus */ - switch (source[index++]) - { - case (ASN_CONTEXT_SPECIFIC | CERT_GOOD): - resp->certStatus[qty] = CERT_GOOD; - index++; - break; - case (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | CERT_REVOKED): - resp->certStatus[qty] = CERT_REVOKED; - GetLength(source, &index, &length, size); - index += length; - break; - case (ASN_CONTEXT_SPECIFIC | CERT_UNKNOWN): - resp->certStatus[qty] = CERT_UNKNOWN; - index++; - break; - default: - return ASN_PARSE_E; - } - - if (source[index++] != ASN_GENERALIZED_TIME) - return ASN_PARSE_E; - - if (GetLength(source, &index, &length, size) < 0) - return ASN_PARSE_E; - resp->thisUpdate = source + index; - index += length; - - remainder = remainder + prevIndex - index; - qty++; + CYASSL_MSG("\tAlloc Cert Status failed"); + return MEMORY_E; + } + cs->next = NULL; */ + + /* Wrapper around the Single Response */ + if (GetSequence(source, &index, &length, size) < 0) + return ASN_PARSE_E; + + /* Wrapper around the CertID */ + if (GetSequence(source, &index, &length, size) < 0) + return ASN_PARSE_E; + /* Skip the hash algorithm */ + if (GetAlgoId(source, &index, &oid, size) < 0) + return ASN_PARSE_E; + /* Skip the hash of CN */ + if (source[index++] != ASN_OCTET_STRING) + return ASN_PARSE_E; + if (GetLength(source, &index, &length, size) < 0) + return ASN_PARSE_E; + index += length; + /* Skip the hash of the issuer public key */ + if (source[index++] != ASN_OCTET_STRING) + return ASN_PARSE_E; + if (GetLength(source, &index, &length, size) < 0) + return ASN_PARSE_E; + index += length; + + /* Read the serial number, it is handled as a string, not as a + * proper number. Just XMEMCPY the data over, rather than load it + * as an mp_int. */ + if (source[index++] != ASN_INTEGER) + return ASN_PARSE_E; + if (GetLength(source, &index, &length, size) < 0) + return ASN_PARSE_E; + if (length <= EXTERNAL_SERIAL_SIZE) + { + if (source[index] == 0) + { + index++; + length--; + } + XMEMCPY(cs->serial, source + index, length); + cs->serialSz = length; + } + else + { + return ASN_GETINT_E; + } + index += length; + + /* CertStatus */ + switch (source[index++]) + { + case (ASN_CONTEXT_SPECIFIC | CERT_GOOD): + cs->status = CERT_GOOD; + index++; + break; + case (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | CERT_REVOKED): + cs->status = CERT_REVOKED; + GetLength(source, &index, &length, size); + index += length; + break; + case (ASN_CONTEXT_SPECIFIC | CERT_UNKNOWN): + cs->status = CERT_UNKNOWN; + index++; + break; + default: + return ASN_PARSE_E; + } + + if (GetBasicDate(source, &index, cs->thisDate, + &cs->thisDateFormat, size) < 0) + return ASN_PARSE_E; + + if (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + { + index++; + if (GetLength(source, &index, &length, size) < 0) + return ASN_PARSE_E; + if (GetBasicDate(source, &index, cs->nextDate, + &cs->nextDateFormat, size) < 0) + return ASN_PARSE_E; + } + if (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) + { + index++; + if (GetLength(source, &index, &length, size) < 0) + return ASN_PARSE_E; + index += length; } - resp->certStatusCount = qty; *ioIndex = index; @@ -4625,6 +4678,7 @@ static int GetNameHash(const byte* source, word32* idx, byte* hash, int maxIdx) { Sha sha; int length; /* length of all distinguished names */ + word32 dummy; CYASSL_ENTER("GetNameHash"); @@ -4638,11 +4692,15 @@ static int GetNameHash(const byte* source, word32* idx, byte* hash, int maxIdx) CYASSL_MSG("Got optional prefix"); } + /* For OCSP, RFC2560 section 4.1.1 states the issuer hash should be + * calculated over the entire DER encoding of the Name field, including + * the tag and length. */ + dummy = *idx; if (GetSequence(source, idx, &length, maxIdx) < 0) return ASN_PARSE_E; InitSha(&sha); - ShaUpdate(&sha, &source[*idx], length); + ShaUpdate(&sha, source + dummy, length + *idx - dummy); ShaFinal(&sha, hash); *idx += length; @@ -4651,32 +4709,6 @@ static int GetNameHash(const byte* source, word32* idx, byte* hash, int maxIdx) } -/* Get raw Date only, no processing, 0 on success */ -static int GetBasicDate(const byte* source, word32* idx, byte* date, - byte* format, int maxIdx) -{ - int length; - - CYASSL_ENTER("GetBasicDate"); - - *format = source[*idx]; - *idx += 1; - if (*format != ASN_UTC_TIME && *format != ASN_GENERALIZED_TIME) - return ASN_TIME_E; - - if (GetLength(source, idx, &length, maxIdx) < 0) - return ASN_PARSE_E; - - if (length > MAX_DATE_SIZE || length < MIN_DATE_SIZE) - return ASN_DATE_SZ_E; - - XMEMCPY(date, &source[*idx], length); - *idx += length; - - return 0; -} - - /* Get Revoked Cert list, 0 on success */ static int GetRevoked(const byte* buff, word32* idx, DecodedCRL* dcrl, int maxIdx) diff --git a/cyassl/ctaocrypt/asn.h b/cyassl/ctaocrypt/asn.h index c0efa7506..fb9f1c758 100644 --- a/cyassl/ctaocrypt/asn.h +++ b/cyassl/ctaocrypt/asn.h @@ -324,6 +324,10 @@ enum cert_enums { #endif /* CYASSL_CERT_GEN */ + +/* for pointer use */ +typedef struct CertStatus CertStatus; + #ifdef HAVE_OCSP enum Ocsp_Response_Status { @@ -349,13 +353,25 @@ enum Ocsp_Sums { }; -#define STATUS_LIST_SIZE 5 - - typedef struct OcspRequest OcspRequest; typedef struct OcspResponse OcspResponse; +struct CertStatus { + CertStatus* next; + + byte serial[EXTERNAL_SERIAL_SIZE]; + int serialSz; + + int status; + + byte thisDate[MAX_DATE_SIZE]; + byte nextDate[MAX_DATE_SIZE]; + byte thisDateFormat; + byte nextDateFormat; +}; + + struct OcspResponse { int responseStatus; /* return code from Responder */ @@ -364,25 +380,17 @@ struct OcspResponse { int version; /* Response version number */ - byte* thisUpdate; /* Time at which this status was set */ - byte* nextUpdate; /* Time for next update */ byte* producedAt; /* Time at which this response was signed */ + byte producedAtFormat;/* format of the producedAt date */ word32 sigIndex; /* Index into source for start of sig */ word32 sigLength; /* Length in octets for the sig */ word32 sigOID; /* OID for hash used for sig */ - int certStatusCount; /* Count of certificate statuses, Note - * 1:1 correspondence between certStatus - * and certSerialNumber */ - byte certSN[STATUS_LIST_SIZE][EXTERNAL_SERIAL_SIZE]; - int certSNsz[STATUS_LIST_SIZE]; - /* Certificate serial number array. */ - word32 certStatus[STATUS_LIST_SIZE]; - /* Certificate status array */ + CertStatus status[1]; /* list of certificate status */ - byte* nonce; - int nonceSz; + byte* nonce; /* pointer to nonce inside ASN.1 response */ + int nonceSz; /* length of the nonce string */ byte* source; /* pointer to source buffer, not owned */ word32 maxIdx; /* max offset based on init size */ @@ -394,8 +402,13 @@ struct OcspRequest { byte* nonce; int nonceSz; - byte* dest; - word32 destSz; + byte* issuerHash; /* pointer to issuerHash in source cert */ + byte* issuerKeyHash; /* pointer to issuerKeyHash in source cert */ + byte* serial; /* pointer to serial number in source cert */ + int serialSz; /* length of the serial number */ + + byte* dest; /* pointer to the destination ASN.1 buffer */ + word32 destSz; /* length of the destination buffer */ void* heap; }; diff --git a/cyassl/internal.h b/cyassl/internal.h index 4a37329e8..e0fe04406 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -624,6 +624,28 @@ CYASSL_LOCAL int UnLockMutex(CyaSSL_Mutex*); +typedef struct OCSP_Entry OCSP_Entry; + +struct OCSP_Entry { + OCSP_Entry* next; /* next entry */ + byte issuerHash[SHA_DIGEST_SIZE]; /* issuer hash */ + byte issuerKeyHash[SHA_DIGEST_SIZE]; /* issuer public key hash */ + CertStatus status[1]; /* OCSP response list */ + int totalStatus; /* number on list */ +}; + + +/* CyaSSL OCSP controller */ +struct CYASSL_OCSP { + byte enabled; + byte useOverrideUrl; + char overrideName[80]; + char overridePath[80]; + int overridePort; + OCSP_Entry ocspList[1]; +}; + + typedef struct CRL_Entry CRL_Entry; /* Complete CRL */ diff --git a/cyassl/ocsp.h b/cyassl/ocsp.h index ee8af1534..2cfd46e25 100644 --- a/cyassl/ocsp.h +++ b/cyassl/ocsp.h @@ -26,38 +26,18 @@ #define CYASSL_OCSP_H +#include #include - #ifdef __cplusplus extern "C" { #endif typedef struct CYASSL_OCSP CYASSL_OCSP; -typedef struct CertStatus CertStatus; - -struct CertStatus { - byte issuerHash[SHA_SIZE]; - byte issuerKeyHash[SHA_SIZE]; - byte serial[EXTERNAL_SERIAL_SIZE]; - int serialSz; - int status; -}; - -struct CYASSL_OCSP { - byte enabled; - byte useOverrideUrl; - char overrideName[80]; - char overridePath[80]; - int overridePort; - int statusLen; - CertStatus status[1]; -}; - - CYASSL_LOCAL int CyaSSL_OCSP_Init(CYASSL_OCSP*); CYASSL_LOCAL void CyaSSL_OCSP_Cleanup(CYASSL_OCSP*); + CYASSL_LOCAL int CyaSSL_OCSP_set_override_url(CYASSL_OCSP*, const char*); CYASSL_LOCAL int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP*, DecodedCert*); diff --git a/src/ocsp.c b/src/ocsp.c index 4f5370ad3..8f65e23f4 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -314,11 +314,10 @@ int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) return OCSP_NEED_URL; } - XMEMCPY(ocsp->status[0].issuerHash, cert->issuerHash, SHA_SIZE); - XMEMCPY(ocsp->status[0].issuerKeyHash, cert->issuerKeyHash, SHA_SIZE); - XMEMCPY(ocsp->status[0].serial, cert->serial, cert->serialSz); - ocsp->status[0].serialSz = cert->serialSz; - ocsp->statusLen = 1; + XMEMCPY(ocsp->ocspList->issuerHash, cert->issuerHash, SHA_SIZE); + XMEMCPY(ocsp->ocspList->issuerKeyHash, cert->issuerKeyHash, SHA_SIZE); + XMEMCPY(ocsp->ocspList->status->serial, cert->serial, cert->serialSz); + ocsp->ocspList->status->serialSz = cert->serialSz; ocspReqSz = EncodeOcspRequest(cert, ocspReqBuf, ocspReqSz); httpBufSz = build_http_request(domainName, path, ocspReqSz, @@ -355,7 +354,7 @@ int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) CYASSL_MSG("OCSP Responder failure"); result = OCSP_LOOKUP_FAIL; } else { - switch (ocspResponse.certStatus[0]) { + switch (ocspResponse.status[0].status) { case CERT_GOOD: result = 0; break;