/* Add the given data to the running hash of the handshake messages */ int32 sslUpdateHSHash(ssl_t *ssl, unsigned char *in, uint32 len) { #ifdef USE_TLS_1_2 /* Keep a running total of each for greatest RFC support when it comes to the CertificateVerify message. Although, trying to be smart about MD5 and SHA-2 based on protocol version */ if ((ssl->majVer == 0 && ssl->minVer == 0) || ssl->minVer == TLS_1_2_MIN_VER) { psSha256Update(&ssl->sec.msgHashSha256, in, len); #ifdef USE_SHA384 psSha384Update(&ssl->sec.msgHashSha384, in, len); #endif } #ifndef USE_ONLY_TLS_1_2 if (ssl->reqMinVer == 0 || ssl->minVer != TLS_1_2_MIN_VER) { psMd5Update(&ssl->sec.msgHashMd5, in, len); } psSha1Update(&ssl->sec.msgHashSha1, in, len); #endif #else psMd5Update(&ssl->sec.msgHashMd5, in, len); psSha1Update(&ssl->sec.msgHashSha1, in, len); #endif return 0; }
/* * Generates all key material. */ int32 sslDeriveKeys(ssl_t *ssl) { psDigestContext_t md5Ctx, sha1Ctx; unsigned char buf[MD5_HASH_SIZE + SHA1_HASH_SIZE]; unsigned char *tmp; uint32 i; /* If this session is resumed, we want to reuse the master secret to regenerate the key block with the new random values. */ if (ssl->flags & SSL_FLAGS_RESUMED) { goto skipPremaster; } /* master_secret = MD5(pre_master_secret + SHA('A' + pre_master_secret + ClientHello.random + ServerHello.random)) + MD5(pre_master_secret + SHA('BB' + pre_master_secret + ClientHello.random + ServerHello.random)) + MD5(pre_master_secret + SHA('CCC' + pre_master_secret + ClientHello.random + ServerHello.random)); */ tmp = ssl->sec.masterSecret; for (i = 0; i < 3; i++) { psSha1Init(&sha1Ctx); psSha1Update(&sha1Ctx, salt[i], i + 1); psSha1Update(&sha1Ctx, ssl->sec.premaster, ssl->sec.premasterSize); psSha1Update(&sha1Ctx, ssl->sec.clientRandom, SSL_HS_RANDOM_SIZE); psSha1Update(&sha1Ctx, ssl->sec.serverRandom, SSL_HS_RANDOM_SIZE); psSha1Final(&sha1Ctx, buf); psMd5Init(&md5Ctx); psMd5Update(&md5Ctx, ssl->sec.premaster, ssl->sec.premasterSize); psMd5Update(&md5Ctx, buf, SHA1_HASH_SIZE); psMd5Final(&md5Ctx, tmp); tmp += MD5_HASH_SIZE; } memset(buf, 0x0, MD5_HASH_SIZE + SHA1_HASH_SIZE); /* premaster is now allocated for DH reasons. Can free here */ psFree(ssl->sec.premaster); ssl->sec.premaster = NULL; ssl->sec.premasterSize = 0; skipPremaster: if (createKeyBlock(ssl, ssl->sec.clientRandom, ssl->sec.serverRandom, ssl->sec.masterSecret, SSL_HS_MASTER_SIZE) < 0) { psTraceInfo("Unable to create key block\n"); return PS_FAILURE; } return SSL_HS_MASTER_SIZE; }
int32_t psHmacMd5(const unsigned char *key, uint16_t keyLen, const unsigned char *buf, uint32_t len, unsigned char hash[MD5_HASHLEN], unsigned char *hmacKey, uint16_t *hmacKeyLen) { psMd5_t md; /* Support for keys larger than 64 bytes. In this case, we take the hash of the key itself and use that instead. Inform the caller by updating the hmacKey and hmacKeyLen outputs */ if (keyLen > 64) { psMd5Init(&md); psMd5Update(&md, key, keyLen); psMd5Final(&md, hash); *hmacKeyLen = MD5_HASHLEN; memcpy(hmacKey, hash, *hmacKeyLen); } else { hmacKey = (unsigned char*)key; *hmacKeyLen = keyLen; } if (HMAC(EVP_md5(), hmacKey, *hmacKeyLen, buf, len, hash, NULL) != NULL) { return PS_SUCCESS; } return PS_FAIL; }
int32_t psHmacMd5Init(psHmacMd5_t *ctx, const unsigned char *key, uint16_t keyLen) { int32_t rc, i; #ifdef CRYPTO_ASSERT psAssert(keyLen <= 64); #endif for (i = 0; (uint32)i < keyLen; i++) { ctx->pad[i] = key[i] ^ 0x36; } for (i = keyLen; i < 64; i++) { ctx->pad[i] = 0x36; } if ((rc = psMd5Init(&ctx->md5)) < 0) { return rc; } psMd5Update(&ctx->md5, ctx->pad, 64); for (i = 0; (uint32)i < keyLen; i++) { ctx->pad[i] = key[i] ^ 0x5c; } for (i = keyLen; i < 64; i++) { ctx->pad[i] = 0x5c; } return PS_SUCCESS; }
/* HMAC-MD5 http://www.faqs.org/rfcs/rfc2104.html the HMAC_MD5 transform looks like: MD5(K XOR opad, MD5(K XOR ipad, text)) where K is an n byte key ipad is the byte 0x36 repeated 64 times opad is the byte 0x5c repeated 64 times and text is the data being protected If the keyLen is > 64 bytes, we hash the key and use it instead */ int32 psHmacMd5(unsigned char *key, uint32 keyLen, const unsigned char *buf, uint32 len, unsigned char *hash, unsigned char *hmacKey, uint32 *hmacKeyLen) { psHmacContext_t ctx; psDigestContext_t md; /* Support for keys larger than 64 bytes. In this case, we take the hash of the key itself and use that instead. Inform the caller by updating the hmacKey and hmacKeyLen outputs */ if (keyLen > 64) { psMd5Init(&md); psMd5Update(&md, key, keyLen); *hmacKeyLen = psMd5Final(&md, hash); memcpy(hmacKey, hash, *hmacKeyLen); } else { hmacKey = key; *hmacKeyLen = keyLen; } psHmacMd5Init(&ctx, hmacKey, *hmacKeyLen); psHmacMd5Update(&ctx, buf, len); return psHmacMd5Final(&ctx, hash); }
void psHmacMd5Update(psHmacMd5_t *ctx, const unsigned char *buf, uint32_t len) { #ifdef CRYPTO_ASSERT psAssert(ctx != NULL && buf != NULL); #endif psMd5Update(&ctx->md5, buf, len); }
int32 psHmacMd5Final(psHmacContext_t *ctx, unsigned char *hash) { psAssert(ctx != NULL); if (hash == NULL) { psTraceCrypto("NULL hash storage passed to psHmacMd5Final\n"); return PS_ARG_FAIL; } psMd5Final(&ctx->u.md5, hash); psMd5Init(&ctx->u.md5); psMd5Update(&ctx->u.md5, ctx->pad, 64); psMd5Update(&ctx->u.md5, hash, MD5_HASH_SIZE); psMd5Final(&ctx->u.md5, hash); memset(ctx->pad, 0x0, 64); return MD5_HASH_SIZE; }
/** Update MD5 and SHA1 hashes of the same data. */ void psMd5Sha1Update(psMd5Sha1_t *md, const unsigned char *buf, uint32_t len) { # ifdef CRYPTO_ASSERT psAssert(md && buf); # endif psMd5Update(&md->md5, buf, len); psSha1Update(&md->sha1, buf, len); }
void psHmacMd5Final(psHmacMd5_t *ctx, unsigned char hash[MD5_HASHLEN]) { int32_t rc; #ifdef CRYPTO_ASSERT psAssert(ctx != NULL); if (hash == NULL) { psTraceCrypto("NULL hash storage passed to psHmacMd5Final\n"); return; } #endif psMd5Final(&ctx->md5, hash); /* This Init should succeed, even if it allocates memory since an psMd5_t was just Finalized the line above */ if ((rc = psMd5Init(&ctx->md5)) < 0) { psAssert(rc >= 0); return; } psMd5Update(&ctx->md5, ctx->pad, 64); psMd5Update(&ctx->md5, hash, MD5_HASHLEN); psMd5Final(&ctx->md5, hash); memset(ctx->pad, 0x0, sizeof(ctx->pad)); }
void psHmacMd5Init(psHmacContext_t *ctx, unsigned char *key, uint32 keyLen) { int32 i; psAssert(keyLen <= 64); for (i = 0; (uint32)i < keyLen; i++) { ctx->pad[i] = key[i] ^ 0x36; } for (i = keyLen; i < 64; i++) { ctx->pad[i] = 0x36; } psMd5Init(&ctx->u.md5); psMd5Update(&ctx->u.md5, ctx->pad, 64); for (i = 0; (uint32)i < keyLen; i++) { ctx->pad[i] = key[i] ^ 0x5c; } for (i = keyLen; i < 64; i++) { ctx->pad[i] = 0x5c; } }
/* Combine the running hash of the handshake mesages with some constants and mix them up a bit more. Output the result to the given buffer. This data will be part of the Finished handshake message. */ int32 sslGenerateFinishedHash(psDigestContext_t *md5, psDigestContext_t *sha1, unsigned char *masterSecret, unsigned char *out, int32 sender) { psDigestContext_t omd5,osha1; unsigned char ihash[SHA1_HASH_SIZE]; /* md5Hash = MD5(master_secret + pad2 + MD5(handshake_messages + sender + master_secret + pad1)); */ if (sender >= 0) { psMd5Update(md5, (sender & SSL_FLAGS_SERVER) ? SENDER_SERVER : SENDER_CLIENT, 4); } psMd5Update(md5, masterSecret, SSL_HS_MASTER_SIZE); psMd5Update(md5, pad1, sizeof(pad1)); psMd5Final(md5, ihash); psMd5Init(&omd5); psMd5Update(&omd5, masterSecret, SSL_HS_MASTER_SIZE); psMd5Update(&omd5, pad2, sizeof(pad2)); psMd5Update(&omd5, ihash, MD5_HASH_SIZE); psMd5Final(&omd5, out); /* The SHA1 hash is generated in the same way, except only 40 bytes of pad1 and pad2 are used. sha1Hash = SHA1(master_secret + pad2 + SHA1(handshake_messages + sender + master_secret + pad1)); */ if (sender >= 0) { psSha1Update(sha1, (sender & SSL_FLAGS_SERVER) ? SENDER_SERVER : SENDER_CLIENT, 4); } psSha1Update(sha1, masterSecret, SSL_HS_MASTER_SIZE); psSha1Update(sha1, pad1, 40); psSha1Final(sha1, ihash); psSha1Init(&osha1); psSha1Update(&osha1, masterSecret, SSL_HS_MASTER_SIZE); psSha1Update(&osha1, pad2, 40); psSha1Update(&osha1, ihash, SHA1_HASH_SIZE); psSha1Final(&osha1, out + MD5_HASH_SIZE); return MD5_HASH_SIZE + SHA1_HASH_SIZE; }
char *websMD5binary(unsigned char *buf, int length) { const char *hex = "0123456789abcdef"; psDigestContext_t md5ctx; unsigned char hash[HASH_SIZE]; char *r, *strReturn; char result[(HASH_SIZE * 2) + 1]; int i; /* * Take the MD5 hash of the string argument. */ psMd5Init(&md5ctx); psMd5Update(&md5ctx, buf, (unsigned int)length); psMd5Final(&md5ctx, hash); /* * Prepare the resulting hash string */ for (i = 0, r = result; i < 16; i++) { *r++ = hex[hash[i] >> 4]; *r++ = hex[hash[i] & 0xF]; } /* * Zero terminate the hash string */ *r = '\0'; /* * Allocate a new copy of the hash string */ i = elementsof(result); strReturn = balloc(B_L, i); strncpy(strReturn, result, i); return strReturn; }
int32_t psHmacMd5(const unsigned char *key, uint16_t keyLen, const unsigned char *buf, uint32_t len, unsigned char hash[MD5_HASHLEN], unsigned char *hmacKey, uint16_t *hmacKeyLen) { int32_t rc; union { psHmacMd5_t mac; psMd5_t md; } u; psHmacMd5_t *mac = &u.mac; psMd5_t *md = &u.md; /* Support for keys larger than 64 bytes. In this case, we take the hash of the key itself and use that instead. Inform the caller by updating the hmacKey and hmacKeyLen outputs */ if (keyLen > 64) { if ((rc = psMd5Init(md)) < 0) { return rc; } psMd5Update(md, key, keyLen); psMd5Final(md, hash); *hmacKeyLen = MD5_HASHLEN; memcpy(hmacKey, hash, *hmacKeyLen); } else { hmacKey = (unsigned char *)key; /* @note typecasting from const */ *hmacKeyLen = keyLen; } if ((rc = psHmacMd5Init(mac, hmacKey, *hmacKeyLen)) < 0) { return rc; } psHmacMd5Update(mac, buf, len); psHmacMd5Final(mac, hash); return PS_SUCCESS; }
/* Generate the key block as follows. '+' indicates concatination. key_block = MD5(master_secret + SHA(`A' + master_secret + ServerHello.random + ClientHello.random)) + MD5(master_secret + SHA(`BB' + master_secret + ServerHello.random + ClientHello.random)) + MD5(master_secret + SHA(`CCC' + master_secret + ServerHello.random + ClientHello.random)) + [...]; */ static int32 createKeyBlock(ssl_t *ssl, unsigned char *clientRandom, unsigned char *serverRandom, unsigned char *masterSecret, uint32 secretLen) { psDigestContext_t md5Ctx, sha1Ctx; unsigned char buf[MD5_HASH_SIZE + SHA1_HASH_SIZE]; unsigned char *tmp; int32 ret = 0; uint32 i, keyIter, reqKeyLen; /* We must generate enough key material to fill the various keys */ reqKeyLen = 2 * ssl->cipher->macSize + 2 * ssl->cipher->keySize + 2 * ssl->cipher->ivSize; /* Find the right number of iterations to make the requested length key block */ keyIter = 1; while (MD5_HASH_SIZE * keyIter < reqKeyLen) { keyIter++; } if (keyIter > sizeof(salt)/sizeof(char*)) { psTraceIntInfo("Error: Not enough salt for key length %d\n", reqKeyLen); return PS_FAILURE; } tmp = ssl->sec.keyBlock; for (i = 0; i < keyIter; i++) { psSha1Init(&sha1Ctx); psSha1Update(&sha1Ctx, salt[i], i + 1); psSha1Update(&sha1Ctx, masterSecret, secretLen); psSha1Update(&sha1Ctx, serverRandom, SSL_HS_RANDOM_SIZE); psSha1Update(&sha1Ctx, clientRandom, SSL_HS_RANDOM_SIZE); psSha1Final(&sha1Ctx, buf); psMd5Init(&md5Ctx); psMd5Update(&md5Ctx, masterSecret, secretLen); psMd5Update(&md5Ctx, buf, SHA1_HASH_SIZE); psMd5Final(&md5Ctx, tmp); tmp += MD5_HASH_SIZE; ret += MD5_HASH_SIZE; } memset(buf, 0x0, MD5_HASH_SIZE + SHA1_HASH_SIZE); /* Client and server use different read/write values, with the Client write value being the server read value. */ if (ssl->flags & SSL_FLAGS_SERVER) { ssl->sec.rMACptr = ssl->sec.keyBlock; ssl->sec.wMACptr = ssl->sec.rMACptr + ssl->cipher->macSize; ssl->sec.rKeyptr = ssl->sec.wMACptr + ssl->cipher->macSize; ssl->sec.wKeyptr = ssl->sec.rKeyptr + ssl->cipher->keySize; ssl->sec.rIVptr = ssl->sec.wKeyptr + ssl->cipher->keySize; ssl->sec.wIVptr = ssl->sec.rIVptr + ssl->cipher->ivSize; } else { ssl->sec.wMACptr = ssl->sec.keyBlock; ssl->sec.rMACptr = ssl->sec.wMACptr + ssl->cipher->macSize; ssl->sec.wKeyptr = ssl->sec.rMACptr + ssl->cipher->macSize; ssl->sec.rKeyptr = ssl->sec.wKeyptr + ssl->cipher->keySize; ssl->sec.wIVptr = ssl->sec.rKeyptr + ssl->cipher->keySize; ssl->sec.rIVptr = ssl->sec.wIVptr + ssl->cipher->ivSize; } return ret; }
void psHmacMd5Update(psHmacContext_t *ctx, const unsigned char *buf, uint32 len) { psAssert(ctx != NULL && buf != NULL); psMd5Update(&ctx->u.md5, buf, len); }
void runDigestTime(psDigestContext_t *ctx, int32 chunk, int32 alg) { psTime_t start, end; unsigned char *dataChunk; unsigned char hashout[64]; int32 bytesSent, bytesToSend, round; #ifdef USE_HIGHRES_TIME int32 mod; int64 diffu; #else int32 diffm; #endif dataChunk = psMalloc(NULL, chunk); bytesToSend = (DATABYTES_AMOUNT / chunk) * chunk; bytesSent = 0; switch (alg) { #ifdef USE_SHA1 case SHA1_ALG: psGetTime(&start, NULL); while (bytesSent < bytesToSend) { psSha1Update(&ctx->sha1, dataChunk, chunk); bytesSent += chunk; } psSha1Final(&ctx->sha1, hashout); psGetTime(&end, NULL); break; #endif #ifdef USE_SHA256 case SHA256_ALG: psGetTime(&start, NULL); while (bytesSent < bytesToSend) { psSha256Update(&ctx->sha256, dataChunk, chunk); bytesSent += chunk; } psSha256Final(&ctx->sha256, hashout); psGetTime(&end, NULL); break; #endif #ifdef USE_SHA384 case SHA384_ALG: psGetTime(&start, NULL); while (bytesSent < bytesToSend) { psSha384Update(&ctx->sha384, dataChunk, chunk); bytesSent += chunk; } psSha384Final(&ctx->sha384, hashout); psGetTime(&end, NULL); break; #endif #ifdef USE_SHA512 case SHA512_ALG: psGetTime(&start, NULL); while (bytesSent < bytesToSend) { psSha512Update(&ctx->sha512, dataChunk, chunk); bytesSent += chunk; } psSha512Final(&ctx->sha512, hashout); psGetTime(&end, NULL); break; #endif #ifdef USE_MD5 case MD5_ALG: psGetTime(&start, NULL); while (bytesSent < bytesToSend) { psMd5Update(&ctx->md5, dataChunk, chunk); bytesSent += chunk; } psMd5Final(&ctx->md5, hashout); psGetTime(&end, NULL); break; #endif default: printf("Skipping Digest Tests\n"); return; } #ifdef USE_HIGHRES_TIME diffu = psDiffUsecs(start, end); round = (bytesToSend / diffu); mod = (bytesToSend % diffu); printf("%d byte chunks in %lld usecs total for rate of %d.%d MB/sec\n", chunk, (unsigned long long)diffu, round, mod); #else diffm = psDiffMsecs(start, end, NULL); round = (bytesToSend / diffm) / 1000; printf("%d byte chunks in %d msecs total for rate of %d MB/sec\n", chunk, diffm, round); #endif }