rfc4346 p_hash
return func(result, secret, label, seed []byte) {
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
pHash(result, secret, labelAndSeed, hashFunc)
}
func prf10(result, secret, label, seed []byte) {
hashSHA1 := sha1.New
hashMD5 := md5.New
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
s1, s2 := splitPreMasterSecret(secret)
pHash(result, s1, labelAndSeed, hashMD5)
result2 := make([]byte, len(result))
pHash(result2, s2, labelAndSeed, hashSHA1)
for i, b := range result2 {
result[i] ^= b
}
}
// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
h := hmac.New(hash, secret)
h.Write(seed)
a := h.Sum(nil)
j := 0
for j < len(result) {
h.Reset()
h.Write(a)
h.Write(seed)
b := h.Sum(nil)
copy(result[j:], b)
j += len(b)
h.Reset()
h.Write(a)
a = h.Sum(nil)
}
}
5. HMAC and the Pseudorandom Function
A number of operations in the TLS record and handshake layer require
a keyed MAC; this is a secure digest of some data protected by a
secret. Forging the MAC is infeasible without knowledge of the MAC
secret. The construction we use for this operation is known as HMAC,
and is described in [HMAC].
HMAC can be used with a variety of different hash algorithms. TLS
uses it in the handshake with two different algorithms, MD5 and SHA-
1, denoting these as HMAC_MD5(secret, data) and HMAC_SHA(secret,
data). Additional hash algorithms can be defined by cipher suites
Dierks & Rescorla Standards Track [Page 12]
RFC 4346 The TLS Protocol April 2006 and used to protect record data, but MD5 and SHA-1 are hard coded into the description of the handshaking for this version of the protocol. In addition, a construction is required to do expansion of secrets into blocks of data for the purposes of key generation or validation. This pseudo-random function (PRF) takes as input a secret, a seed, and an identifying label and produces an output of arbitrary length. In order to make the PRF as secure as possible, it uses two hash algorithms in a way that should guarantee its security if either algorithm remains secure. First, we define a data expansion function, P_hash(secret, data) that uses a single hash function to expand a secret and seed into an arbitrary quantity of output: P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + HMAC_hash(secret, A(2) + seed) + HMAC_hash(secret, A(3) + seed) + ... Where + indicates concatenation. A() is defined as: A(0) = seed A(i) = HMAC_hash(secret, A(i-1)) P_hash can be iterated as many times as is necessary to produce the required quantity of data. For example, if P_SHA-1 is being used to create 64 bytes of data, it will have to be iterated 4 times (through A(4)), creating 80 bytes of output data; the last 16 bytes of the final iteration will then be discarded, leaving 64 bytes of output data. TLS's PRF is created by splitting the secret into two halves and using one half to generate data with P_MD5 and the other half to generate data with P_SHA-1, then exclusive-ORing the outputs of these two expansion functions together. S1 and S2 are the two halves of the secret, and each is the same length. S1 is taken from the first half of the secret, S2 from the second half. Their length is created by rounding up the length of the overall secret, divided by two; thus, if the original secret is an odd number of bytes long, the last byte of S1 will be the same as the first byte of S2. Dierks & Rescorla Standards Track [Page 13]
RFC 4346 The TLS Protocol April 2006 L_S = length in bytes of secret; L_S1 = L_S2 = ceil(L_S / 2); The secret is partitioned into two halves (with the possibility of one shared byte) as described above, S1 taking the first L_S1 bytes, and S2 the last L_S2 bytes. The PRF is then defined as the result of mixing the two pseudorandom streams by exclusive-ORing them together. PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed); The label is an ASCII string. It should be included in the exact form it is given without a length byte or trailing null character. For example, the label "slithy toves" would be processed by hashing the following bytes: 73 6C 69 74 68 79 20 74 6F 76 65 73 Note that because MD5 produces 16-byte outputs and SHA-1 produces 20-byte outputs, the boundaries of their internal iterations will not be aligned. Generating an 80-byte output will require that P_MD5 iterate through A(5), while P_SHA-1 will only iterate through A(4).
static int tls1_prf_P_hash(const EVP_MD *md,
const unsigned char *sec, size_t sec_len,
const unsigned char *seed, size_t seed_len,
unsigned char *out, size_t olen)
{
int chunk;
EVP_MD_CTX *ctx = NULL, *ctx_tmp = NULL, *ctx_init = NULL;
EVP_PKEY *mac_key = NULL;
unsigned char A1[EVP_MAX_MD_SIZE];
size_t A1_len;
int ret = 0;
chunk = EVP_MD_size(md);
OPENSSL_assert(chunk >= 0);
ctx = EVP_MD_CTX_new();
ctx_tmp = EVP_MD_CTX_new();
ctx_init = EVP_MD_CTX_new();
if (ctx == NULL || ctx_tmp == NULL || ctx_init == NULL)
goto err;
EVP_MD_CTX_set_flags(ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len);
if (mac_key == NULL)
goto err;
if (!EVP_DigestSignInit(ctx_init, NULL, md, NULL, mac_key))
goto err;
if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))
goto err;
if (seed != NULL && !EVP_DigestSignUpdate(ctx, seed, seed_len))
goto err;
if (!EVP_DigestSignFinal(ctx, A1, &A1_len))
goto err;
for (;;) {
/* Reinit mac contexts */
if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))
goto err;
if (!EVP_DigestSignUpdate(ctx, A1, A1_len))
goto err;
if (olen > (size_t)chunk && !EVP_MD_CTX_copy_ex(ctx_tmp, ctx))
goto err;
if (seed && !EVP_DigestSignUpdate(ctx, seed, seed_len))
goto err;
if (olen > (size_t)chunk) {
size_t mac_len;
if (!EVP_DigestSignFinal(ctx, out, &mac_len))
goto err;
out += mac_len;
olen -= mac_len;
/* calc the next A1 value */
if (!EVP_DigestSignFinal(ctx_tmp, A1, &A1_len))
goto err;
} else { /* last one */
if (!EVP_DigestSignFinal(ctx, A1, &A1_len))
goto err;
memcpy(out, A1, olen);
break;
}
}
ret = 1;
err:
EVP_PKEY_free(mac_key);
EVP_MD_CTX_free(ctx);
EVP_MD_CTX_free(ctx_tmp);
EVP_MD_CTX_free(ctx_init);
OPENSSL_cleanse(A1, sizeof(A1));
return ret;
}
static int tls1_prf_alg(const EVP_MD *md,
const unsigned char *sec, size_t slen,
const unsigned char *seed, size_t seed_len,
unsigned char *out, size_t olen)
{
#if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_SHA)
if (EVP_MD_type(md) == NID_md5_sha1) {
size_t i;
unsigned char *tmp;
if (!tls1_prf_P_hash(EVP_md5(), sec, slen/2 + (slen & 1),
seed, seed_len, out, olen))
return 0;
tmp = OPENSSL_malloc(olen);
if (tmp == NULL)
return 0;
if (!tls1_prf_P_hash(EVP_sha1(), sec + slen/2, slen/2 + (slen & 1),
seed, seed_len, tmp, olen)) {
OPENSSL_clear_free(tmp, olen);
return 0;
}
for (i = 0; i < olen; i++)
out[i] ^= tmp[i];
OPENSSL_clear_free(tmp, olen);
return 1;
}
#endif
if (!tls1_prf_P_hash(md, sec, slen, seed, seed_len, out, olen))
return 0;
return 1;
}