From 58589b4f9b4bc3e5b2df4a5110f27f09dabd83f8 Mon Sep 17 00:00:00 2001 From: Todd A Ouska Date: Mon, 11 Apr 2011 12:55:17 -0700 Subject: [PATCH] add verify from root, top->down, serial number extension for size --- ctaocrypt/include/asn.h | 4 +- ctaocrypt/src/asn.c | 51 +++++++++------- examples/client/client.c | 3 +- include/cyassl_int.h | 5 +- include/openssl/cyassl_test.h | 7 ++- include/openssl/ssl.h | 3 +- src/cyassl_int.c | 105 ++++++++++++++++++------------- src/ssl.c | 112 ++++++++++++++++++++++++++-------- 8 files changed, 194 insertions(+), 96 deletions(-) diff --git a/ctaocrypt/include/asn.h b/ctaocrypt/include/asn.h index 431f8930c..5cd02a677 100644 --- a/ctaocrypt/include/asn.h +++ b/ctaocrypt/include/asn.h @@ -42,6 +42,7 @@ enum { SUBJECT = 1, SERIAL_SIZE = 8, + EXTERNAL_SERIAL_SIZE = 32, BEFORE = 0, AFTER = 1 @@ -188,7 +189,8 @@ typedef struct DecodedCert { byte* source; /* byte buffer holder cert, NOT owner */ word32 srcIdx; /* current offset into buffer */ void* heap; /* for user memory overrides */ - byte serial[SERIAL_SIZE]; /* raw serial number */ + byte serial[EXTERNAL_SERIAL_SIZE]; /* raw serial number */ + int serialSz; /* raw serial bytes stored */ #ifdef CYASSL_CERT_GEN /* easy access to sujbect info for other sign */ char* subjectSN; diff --git a/ctaocrypt/src/asn.c b/ctaocrypt/src/asn.c index eca8a353a..10d391cfc 100644 --- a/ctaocrypt/src/asn.c +++ b/ctaocrypt/src/asn.c @@ -828,7 +828,8 @@ void InitDecodedCert(DecodedCert* cert, byte* source, void* heap) cert->source = source; /* don't own */ cert->srcIdx = 0; cert->heap = heap; - XMEMSET(cert->serial, 0, SERIAL_SIZE); + XMEMSET(cert->serial, 0, EXTERNAL_SERIAL_SIZE); + cert->serialSz = 0; #ifdef CYASSL_CERT_GEN cert->subjectSN = 0; cert->subjectSNLen = 0; @@ -861,6 +862,7 @@ static int GetCertHeader(DecodedCert* cert, word32 inSz) { int ret = 0, version, len; word32 begin = cert->srcIdx; + byte serialTmp[EXTERNAL_SERIAL_SIZE]; mp_int mpi; if (GetSequence(cert->source, &cert->srcIdx, &len) < 0) @@ -880,11 +882,14 @@ static int GetCertHeader(DecodedCert* cert, word32 inSz) return ASN_PARSE_E; len = mp_unsigned_bin_size(&mpi); - if (len > SERIAL_SIZE) - len = SERIAL_SIZE; /* use first 64 bits for unique id */ - if (mp_to_unsigned_bin(&mpi, cert->serial + (SERIAL_SIZE - len)) != MP_OKAY) - ret = MP_TO_E; - + if (len < sizeof(serialTmp)) { + if (mp_to_unsigned_bin(&mpi, serialTmp) == MP_OKAY) { + if (len > EXTERNAL_SERIAL_SIZE) + len = EXTERNAL_SERIAL_SIZE; + XMEMCPY(cert->serial, serialTmp, len); + cert->serialSz = len; + } + } mp_clear(&mpi); return ret; } @@ -1632,8 +1637,10 @@ static int ConfirmSignature(DecodedCert* cert, const byte* key, word32 keySz, digestSz = SHA256_DIGEST_SIZE; } #endif - else + else { + // TAO CYASSL_MSG("Verify Signautre has unsupported type"); return 0; /* ASN_SIG_HASH_E; */ + } if (keyOID == RSAk) { RsaKey pubKey; @@ -1746,6 +1753,10 @@ int ParseCert(DecodedCert* cert, word32 inSz, int type, int verify, } +/* from SSL proper, for locking can't do find here anymore */ +Signer* GetCA(Signer* signers, byte* hash); + + int ParseCertRelative(DecodedCert* cert, word32 inSz, int type, int verify, Signer* signers) { @@ -1774,23 +1785,20 @@ int ParseCertRelative(DecodedCert* cert, word32 inSz, int type, int verify, 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; + Signer* signer = GetCA(signers, cert->issuerHash); + + if (signer) { + /* try to confirm/verify signature */ + if (!ConfirmSignature(cert, signers->publicKey, + signers->pubKeySize, signers->keyOID)) + return ASN_SIG_CONFIRM_E; } - if (!confirm) + else { + /* no signer */ return ASN_SIG_CONFIRM_E; + } } + if (badDate != 0) return badDate; @@ -2219,6 +2227,7 @@ void InitCert(Cert* cert) cert->bodySz = 0; cert->keyType = RSA_KEY; XMEMSET(cert->serial, 0, SERIAL_SIZE); + cert->serialSz = 0; cert->issuer.country[0] = '\0'; cert->issuer.state[0] = '\0'; diff --git a/examples/client/client.c b/examples/client/client.c index ee0d6a4a3..63cf3bad1 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -160,7 +160,8 @@ void client_test(void* args) ssl = SSL_new(ctx); SSL_set_fd(ssl, sockfd); - CyaSSL_check_domain_name(ssl, "www.yassl.com"); + if (argc != 3) + CyaSSL_check_domain_name(ssl, "www.yassl.com"); #ifdef NON_BLOCKING tcp_set_nonblocking(&sockfd); NonBlockingSSL_Connect(ssl); diff --git a/include/cyassl_int.h b/include/cyassl_int.h index 9cd7c01d8..c1abb8ba2 100644 --- a/include/cyassl_int.h +++ b/include/cyassl_int.h @@ -619,6 +619,8 @@ void SSL_CtxResourceFree(SSL_CTX*); int DeriveTlsKeys(SSL* ssl); int ProcessOldClientHello(SSL* ssl, const byte* input, word32* inOutIdx, word32 inSz, word16 sz); +int AddCA(SSL_CTX* ctx, buffer der, SSL*); +int IsCA(SSL_CTX* ctx, byte* hash); /* All cipher suite related info */ typedef struct CipherSpecs { @@ -933,7 +935,8 @@ struct X509_NAME { struct X509 { X509_NAME issuer; X509_NAME subject; - byte serial[SERIAL_SIZE]; + int serialSz; + byte serial[EXTERNAL_SERIAL_SIZE]; }; diff --git a/include/openssl/cyassl_test.h b/include/openssl/cyassl_test.h index 8e0a458cc..951cad017 100644 --- a/include/openssl/cyassl_test.h +++ b/include/openssl/cyassl_test.h @@ -174,16 +174,17 @@ static INLINE void showPeer(SSL* ssl) if (peer) { char* issuer = X509_NAME_oneline(X509_get_issuer_name(peer), 0, 0); char* subject = X509_NAME_oneline(X509_get_subject_name(peer), 0, 0); - byte serial[SERIAL_SZ]; + byte serial[32]; int ret; + int sz = sizeof(serial); printf("peer's cert info:\n issuer : %s\n subject: %s\n", issuer, subject); - ret = CyaSSL_X509_get_serial_number(peer, serial); + ret = CyaSSL_X509_get_serial_number(peer, serial, &sz); if (ret == 0) { int i; printf(" serial number"); - for (i = 0; i < sizeof(serial); i++) + for (i = 0; i < sz; i++) printf(":%02x", serial[i]); printf("\n"); } diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 7f19fa12f..ba08dd443 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -385,7 +385,6 @@ enum { OCSP_BASICRESP = 16, ASN1_GENERALIZEDTIME = 4, - SERIAL_SZ = 8, SSL_OP_MICROSOFT_SESS_ID_BUG = 1, SSL_OP_NETSCAPE_CHALLENGE_BUG = 2, @@ -624,7 +623,7 @@ unsigned char* CyaSSL_get_chain_cert(X509_CHAIN*, int idx); /* index cert */ int CyaSSL_get_chain_cert_pem(X509_CHAIN*, int idx, unsigned char* buffer, int inLen, int* outLen); /* get index cert in PEM */ const unsigned char* CyaSSL_get_sessionID(const SSL_SESSION* session); -int CyaSSL_X509_get_serial_number(X509*, unsigned char*); +int CyaSSL_X509_get_serial_number(X509*, unsigned char*, int*); #ifndef _WIN32 #ifndef NO_WRITEV diff --git a/src/cyassl_int.c b/src/cyassl_int.c index aa1914298..b1b71cb0d 100644 --- a/src/cyassl_int.c +++ b/src/cyassl_int.c @@ -1390,8 +1390,11 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) word32 listSz, i = *inOutIdx; int ret = 0; int anyError = 0; - int firstTime = 1; /* peer's is at front */ + int firstTime = 1; /* peer's is at front */ + int totalCerts = 0; /* number of certs in certs buffer */ + int count; char domain[ASN_NAME_MAX]; + buffer certs[MAX_CHAIN_DEPTH]; #ifdef CYASSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo); @@ -1399,13 +1402,16 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) #endif c24to32(&input[i], &listSz); i += CERT_HEADER_SZ; - + + CYASSL_MSG("Loading peer's cert chain"); + /* first put cert chain into buffer so can verify top down + we're sent bottom up */ while (listSz) { /* cert size */ - buffer myCert; - word32 certSz; - DecodedCert dCert; - word32 idx = 0; + word32 certSz; + + if (totalCerts >= MAX_CHAIN_DEPTH) + return BUFFER_E; c24to32(&input[i], &certSz); i += CERT_HEADER_SZ; @@ -1413,49 +1419,70 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) if (listSz > MAX_RECORD_SIZE || certSz > MAX_RECORD_SIZE) return BUFFER_E; - myCert.length = certSz; - myCert.buffer = input + i; - i += certSz; - - listSz -= certSz + CERT_HEADER_SZ; - - if (ret != 0 && anyError == 0) - anyError = ret; /* save error from last time */ + certs[totalCerts].length = certSz; + certs[totalCerts].buffer = input + i; #ifdef SESSION_CERTS if (ssl->session.chain.count < MAX_CHAIN_DEPTH && - myCert.length < MAX_X509_SIZE) { - ssl->session.chain.certs[ssl->session.chain.count].length = - myCert.length; + certSz < MAX_X509_SIZE) { + ssl->session.chain.certs[ssl->session.chain.count].length = certSz; XMEMCPY(ssl->session.chain.certs[ssl->session.chain.count].buffer, - myCert.buffer, myCert.length); + input + i, certSz); ssl->session.chain.count++; } else { CYASSL_MSG("Couldn't store chain cert for session"); } #endif + i += certSz; + listSz -= certSz + CERT_HEADER_SZ; + + totalCerts++; + CYASSL_MSG(" Put another cert into chain"); + } + + count = totalCerts; + + /* verify up to peer's first */ + while (count > 1) { + buffer myCert = certs[count - 1]; + DecodedCert dCert; + InitDecodedCert(&dCert, myCert.buffer, ssl->heap); ret = ParseCertRelative(&dCert, myCert.length, CERT_TYPE, !ssl->options.verifyNone, ssl->caList); + if (ret == 0 && !IsCA(ssl->ctx, dCert.subjectHash)) { + buffer add; + add.length = myCert.length; + add.buffer = (byte*)XMALLOC(myCert.length, ssl->heap, + DYNAMIC_TYPE_CA); + CYASSL_MSG("Adding CA from chain"); - if (!firstTime) { - FreeDecodedCert(&dCert); - continue; + if (add.buffer == NULL) + return MEMORY_E; + XMEMCPY(add.buffer, myCert.buffer, myCert.length); + + ret = AddCA(ssl->ctx, add, ssl); + if (ret == 1) ret = 0; /* SSL_SUCCESS for external */ } - /* get rest of peer info in case user wants to continue */ - if (ret != 0) { - if (!(ret == ASN_BEFORE_DATE_E || ret == ASN_AFTER_DATE_E || - ret == ASN_SIG_CONFIRM_E)) { - FreeDecodedCert(&dCert); - continue; - } - } - - /* first one has peer's key */ - firstTime = 0; + if (ret != 0 && anyError == 0) + anyError = ret; /* save error from last time */ + FreeDecodedCert(&dCert); + count--; + } + + /* peer's, may not have one if blank client cert sent by TLSv1.2 */ + if (count) { + buffer myCert = certs[0]; + DecodedCert dCert; + + CYASSL_MSG("Veriying Peer's cert"); + + InitDecodedCert(&dCert, myCert.buffer, ssl->heap); + ret = ParseCertRelative(&dCert, myCert.length, CERT_TYPE, + !ssl->options.verifyNone, ssl->caList); ssl->options.havePeerCert = 1; /* set X509 format */ #ifdef OPENSSL_EXTRA @@ -1463,7 +1490,8 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) XSTRNCPY(ssl->peerCert.issuer.name, dCert.issuer, ASN_NAME_MAX); ssl->peerCert.subject.sz = (int)XSTRLEN(dCert.subject) + 1; XSTRNCPY(ssl->peerCert.subject.name, dCert.subject, ASN_NAME_MAX); - XMEMCPY(ssl->peerCert.serial, dCert.serial, SERIAL_SIZE); + XMEMCPY(ssl->peerCert.serial, dCert.serial, EXTERNAL_SERIAL_SIZE); + ssl->peerCert.serialSz = dCert.serialSz; #endif XMEMCPY(domain, dCert.subjectCN, dCert.subjectCNLen); @@ -1478,11 +1506,10 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) /* decode peer key */ if (dCert.keyOID == RSAk) { + word32 idx = 0; if (RsaPublicKeyDecode(dCert.publicKey, &idx, &ssl->peerRsaKey, dCert.pubKeySize) != 0) { ret = PEER_KEY_ERROR; - FreeDecodedCert(&dCert); - continue; } ssl->peerRsaKeyPresent = 1; } @@ -1490,8 +1517,6 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) else if (dCert.keyOID == NTRUk) { if (dCert.pubKeySize > sizeof(ssl->peerNtruKey)) { ret = PEER_KEY_ERROR; - FreeDecodedCert(&dCert); - continue; } XMEMCPY(ssl->peerNtruKey, dCert.publicKey, dCert.pubKeySize); ssl->peerNtruKeyLen = (word16)dCert.pubKeySize; @@ -1503,8 +1528,6 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) if (ecc_import_x963(dCert.publicKey, dCert.pubKeySize, &ssl->peerEccDsaKey) != 0) { ret = PEER_KEY_ERROR; - FreeDecodedCert(&dCert); - continue; } ssl->peerEccDsaKeyPresent = 1; } @@ -1512,8 +1535,8 @@ static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) FreeDecodedCert(&dCert); } - - if (anyError != 0) + + if (anyError != 0 && ret == 0) ret = anyError; if (ret == 0 && ssl->options.side == CLIENT_END) diff --git a/src/ssl.c b/src/ssl.c index 01bc509e3..6131a4145 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -290,15 +290,61 @@ int SSL_pending(SSL* ssl) } -/* owns der */ -static int AddCA(SSL_CTX* ctx, buffer der) +static CyaSSL_Mutex ca_mutex; /* CA signers mutex */ + +/* does CA already exist on list */ +int IsCA(SSL_CTX* ctx, byte* hash) +{ + Signer* signers; + int ret = 0; + + if (LockMutex(&ca_mutex) != 0) + return ret; + signers = ctx->caList; + while (signers) { + if (XMEMCMP(hash, signers->hash, SHA_DIGEST_SIZE) == 0) { + ret = 1; + break; + } + signers = signers->next; + } + UnLockMutex(&ca_mutex); + + return ret; +} + + +/* return CA if found, otherwise NULL */ +Signer* GetCA(Signer* signers, byte* hash) +{ + Signer* ret = 0; + + if (LockMutex(&ca_mutex) != 0) + return ret; + while (signers) { + if (XMEMCMP(hash, signers->hash, SHA_DIGEST_SIZE) == 0) { + ret = signers; + break; + } + signers = signers->next; + } + UnLockMutex(&ca_mutex); + + return ret; +} + + +/* owns der, cyassl_int now uses too */ +int AddCA(SSL_CTX* ctx, buffer der, SSL* ssl) { word32 ret; DecodedCert cert; Signer* signer = 0; + CYASSL_MSG("Adding a CA"); InitDecodedCert(&cert, der.buffer, ctx->heap); ret = ParseCert(&cert, der.length, CA_TYPE, ctx->verifyPeer, 0); + CYASSL_MSG(" Parsed new CA"); if (ret == 0) { /* take over signer parts */ @@ -315,13 +361,23 @@ static int AddCA(SSL_CTX* ctx, buffer der) cert.publicKey = 0; /* don't free here */ cert.subjectCN = 0; - signer->next = ctx->caList; - ctx->caList = signer; /* takes ownership */ + if (LockMutex(&ca_mutex) == 0) { + signer->next = ctx->caList; + ctx->caList = signer; /* takes ownership */ + if (ssl) + ssl->caList = ctx->caList; + UnLockMutex(&ca_mutex); + } + else + FreeSigners(signer, ctx->heap); } } + CYASSL_MSG(" Freeing Parsed CA"); FreeDecodedCert(&cert); + CYASSL_MSG(" Freeing der CA"); XFREE(der.buffer, ctx->heap, DYNAMIC_TYPE_CA); + CYASSL_MSG(" OK Freeing der CA"); if (ret == 0) return SSL_SUCCESS; return ret; @@ -359,7 +415,7 @@ static int AddCA(SSL_CTX* ctx, buffer der) static SessionRow SessionCache[SESSION_ROWS]; - static CyaSSL_Mutex mutex; /* SessionCache mutex */ + static CyaSSL_Mutex session_mutex; /* SessionCache mutex */ #endif /* NO_SESSION_CACHE */ @@ -663,7 +719,7 @@ static int AddCA(SSL_CTX* ctx, buffer der) #endif /* OPENSSL_EXTRA || HAVE_WEBSERVER */ if (type == CA_TYPE) - return AddCA(ctx, der); /* takes der over */ + return AddCA(ctx, der, ssl); /* takes der over */ else if (type == CERT_TYPE) { if (ssl) { if (ssl->buffers.weOwnCert && ssl->buffers.certificate.buffer) @@ -800,6 +856,7 @@ static int ProcessFile(SSL_CTX* ctx, const char* fname, int format, int type, XREWIND(file); if (sz > (long)sizeof(staticBuffer)) { + CYASSL_MSG("Getting dynamic buffer"); buffer = (byte*) XMALLOC(sz, ctx->heap, DYNAMIC_TYPE_FILE); if (buffer == NULL) { XFCLOSE(file); @@ -1488,27 +1545,29 @@ int SSL_CTX_set_cipher_list(SSL_CTX* ctx, const char* list) int InitCyaSSL(void) { + int ret = 0; #ifndef NO_SESSION_CACHE - if (InitMutex(&mutex) == 0) - return 0; - else - return -1; -#else - return 0; + if (InitMutex(&session_mutex) != 0) + ret = -1; #endif + if (InitMutex(&ca_mutex) != 0) + ret = -1; + + return ret; } int FreeCyaSSL(void) { + int ret = 0; #ifndef NO_SESSION_CACHE - if (FreeMutex(&mutex) == 0) - return 0; - else - return -1; -#else - return 0; + if (FreeMutex(&session_mutex) != 0) + ret = -1; #endif + if (FreeMutex(&ca_mutex) != 0) + ret = -1; + + return ret; } @@ -1541,7 +1600,7 @@ SSL_SESSION* GetSession(SSL* ssl, byte* masterSecret) row = HashSession(id) % SESSION_ROWS; - if (LockMutex(&mutex) != 0) + if (LockMutex(&session_mutex) != 0) return 0; if (SessionCache[row].totalCount >= SESSIONS_PER_ROW) @@ -1566,7 +1625,7 @@ SSL_SESSION* GetSession(SSL* ssl, byte* masterSecret) } } - UnLockMutex(&mutex); + UnLockMutex(&session_mutex); return ret; } @@ -1602,7 +1661,7 @@ int AddSession(SSL* ssl) row = HashSession(ssl->arrays.sessionID) % SESSION_ROWS; - if (LockMutex(&mutex) != 0) + if (LockMutex(&session_mutex) != 0) return -1; idx = SessionCache[row].nextIdx++; @@ -1629,7 +1688,7 @@ int AddSession(SSL* ssl) if (SessionCache[row].nextIdx == SESSIONS_PER_ROW) SessionCache[row].nextIdx = 0; - if (UnLockMutex(&mutex) != 0) + if (UnLockMutex(&session_mutex) != 0) return -1; return 0; @@ -3619,14 +3678,15 @@ int CyaSSL_set_compression(SSL* ssl) } /* write X509 serial number in unsigned binary to buffer - buffer needs to be at least SERIAL_SIZE + buffer needs to be at least EXTERNAL_SERIAL_SIZE (32) for all cases return 0 on success */ - int CyaSSL_X509_get_serial_number(X509* x509, byte* buffer) + int CyaSSL_X509_get_serial_number(X509* x509, byte* buffer, int* inOutSz) { - if (x509 == NULL || buffer == NULL) + if (x509 == NULL || buffer == NULL || *inOutSz < x509->serialSz) return -1; - XMEMCPY(buffer, x509->serial, SERIAL_SIZE); + XMEMCPY(buffer, x509->serial, x509->serialSz); + *inOutSz = x509->serialSz; return 0; }