michael@0: /* tlsprfalg.c - TLS Pseudo Random Function (PRF) implementation michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: michael@0: #include "blapi.h" michael@0: #include "hasht.h" michael@0: #include "alghmac.h" michael@0: michael@0: michael@0: #define PHASH_STATE_MAX_LEN HASH_LENGTH_MAX michael@0: michael@0: /* TLS P_hash function */ michael@0: SECStatus michael@0: TLS_P_hash(HASH_HashType hashType, const SECItem *secret, const char *label, michael@0: SECItem *seed, SECItem *result, PRBool isFIPS) michael@0: { michael@0: unsigned char state[PHASH_STATE_MAX_LEN]; michael@0: unsigned char outbuf[PHASH_STATE_MAX_LEN]; michael@0: unsigned int state_len = 0, label_len = 0, outbuf_len = 0, chunk_size; michael@0: unsigned int remaining; michael@0: unsigned char *res; michael@0: SECStatus status; michael@0: HMACContext *cx; michael@0: SECStatus rv = SECFailure; michael@0: const SECHashObject *hashObj = HASH_GetRawHashObject(hashType); michael@0: michael@0: PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len)); michael@0: PORT_Assert((seed != NULL) && (seed->data != NULL)); michael@0: PORT_Assert((result != NULL) && (result->data != NULL)); michael@0: michael@0: remaining = result->len; michael@0: res = result->data; michael@0: michael@0: if (label != NULL) michael@0: label_len = PORT_Strlen(label); michael@0: michael@0: cx = HMAC_Create(hashObj, secret->data, secret->len, isFIPS); michael@0: if (cx == NULL) michael@0: goto loser; michael@0: michael@0: /* initialize the state = A(1) = HMAC_hash(secret, seed) */ michael@0: HMAC_Begin(cx); michael@0: HMAC_Update(cx, (unsigned char *)label, label_len); michael@0: HMAC_Update(cx, seed->data, seed->len); michael@0: status = HMAC_Finish(cx, state, &state_len, sizeof(state)); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* generate a block at a time until we're done */ michael@0: while (remaining > 0) { michael@0: michael@0: HMAC_Begin(cx); michael@0: HMAC_Update(cx, state, state_len); michael@0: if (label_len) michael@0: HMAC_Update(cx, (unsigned char *)label, label_len); michael@0: HMAC_Update(cx, seed->data, seed->len); michael@0: status = HMAC_Finish(cx, outbuf, &outbuf_len, sizeof(outbuf)); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* Update the state = A(i) = HMAC_hash(secret, A(i-1)) */ michael@0: HMAC_Begin(cx); michael@0: HMAC_Update(cx, state, state_len); michael@0: status = HMAC_Finish(cx, state, &state_len, sizeof(state)); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: chunk_size = PR_MIN(outbuf_len, remaining); michael@0: PORT_Memcpy(res, &outbuf, chunk_size); michael@0: res += chunk_size; michael@0: remaining -= chunk_size; michael@0: } michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: loser: michael@0: /* clear out state so it's not left on the stack */ michael@0: if (cx) michael@0: HMAC_Destroy(cx, PR_TRUE); michael@0: PORT_Memset(state, 0, sizeof(state)); michael@0: PORT_Memset(outbuf, 0, sizeof(outbuf)); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: TLS_PRF(const SECItem *secret, const char *label, SECItem *seed, michael@0: SECItem *result, PRBool isFIPS) michael@0: { michael@0: SECStatus rv = SECFailure, status; michael@0: unsigned int i; michael@0: SECItem tmp = { siBuffer, NULL, 0}; michael@0: SECItem S1; michael@0: SECItem S2; michael@0: michael@0: PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len)); michael@0: PORT_Assert((seed != NULL) && (seed->data != NULL)); michael@0: PORT_Assert((result != NULL) && (result->data != NULL)); michael@0: michael@0: S1.type = siBuffer; michael@0: S1.len = (secret->len / 2) + (secret->len & 1); michael@0: S1.data = secret->data; michael@0: michael@0: S2.type = siBuffer; michael@0: S2.len = S1.len; michael@0: S2.data = secret->data + (secret->len - S2.len); michael@0: michael@0: tmp.data = (unsigned char*)PORT_Alloc(result->len); michael@0: if (tmp.data == NULL) michael@0: goto loser; michael@0: tmp.len = result->len; michael@0: michael@0: status = TLS_P_hash(HASH_AlgMD5, &S1, label, seed, result, isFIPS); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: status = TLS_P_hash(HASH_AlgSHA1, &S2, label, seed, &tmp, isFIPS); michael@0: if (status != SECSuccess) michael@0: goto loser; michael@0: michael@0: for (i = 0; i < result->len; i++) michael@0: result->data[i] ^= tmp.data[i]; michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: loser: michael@0: if (tmp.data != NULL) michael@0: PORT_ZFree(tmp.data, tmp.len); michael@0: return rv; michael@0: } michael@0: