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