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