Commit 0f52d9ed authored by David Makepeace's avatar David Makepeace Committed by Shane Lontis
Browse files

Cleaned up tls1_prf and added comments.

parent e7018588
Loading
Loading
Loading
Loading
+117 −35
Original line number Diff line number Diff line
@@ -7,6 +7,44 @@
 * https://www.openssl.org/source/license.html
 */

/*
 * Refer to "The TLS Protocol Version 1.0" Section 5
 * (https://tools.ietf.org/html/rfc2246#section-5) and
 * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
 * (https://tools.ietf.org/html/rfc5246#section-5).
 *
 * For TLS v1.0 and TLS v1.1 the TLS PRF algorithm is given by:
 *
 *   PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
 *                              P_SHA-1(S2, label + seed)
 *
 * where P_MD5 and P_SHA-1 are defined by P_<hash>, below, and S1 and S2 are
 * two halves of the secret (with the possibility of one shared byte, in the
 * case where the length of the original secret is odd).  S1 is taken from the
 * first half of the secret, S2 from the second half.
 *
 * For TLS v1.2 the TLS PRF algorithm is given by:
 *
 *   PRF(secret, label, seed) = P_<hash>(secret, label + seed)
 *
 * where hash is SHA-256 for all cipher suites defined in RFC 5246 as well as
 * those published prior to TLS v1.2 while the TLS v1.2 protocol is in effect,
 * unless defined otherwise by the cipher suite.
 *
 * P_<hash> is an expansion function 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.  P_<hash> can be iterated as many times as
 * is necessary to produce the required quantity of data.
 *
 * A(i) is defined as:
 *     A(0) = seed
 *     A(i) = HMAC_<hash>(secret, A(i-1))
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
@@ -168,25 +206,41 @@ const EVP_KDF tls1_prf_kdf_meth = {
    kdf_tls1_prf_derive
};

/*
 * Refer to "The TLS Protocol Version 1.0" Section 5
 * (https://tools.ietf.org/html/rfc2246#section-5) and
 * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
 * (https://tools.ietf.org/html/rfc5246#section-5).
 *
 * P_<hash> is an expansion function 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.  P_<hash> can be iterated as many times as
 * is necessary to produce the required quantity of data.
 *
 * A(i) is defined as:
 *     A(0) = seed
 *     A(i) = HMAC_<hash>(secret, A(i-1))
 */
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_MAC_CTX *ctx = NULL, *ctx_tmp = NULL, *ctx_init = NULL;
    unsigned char A1[EVP_MAX_MD_SIZE];
    size_t A1_len;
    size_t chunk;
    EVP_MAC_CTX *ctx = NULL, *ctx_Ai = NULL, *ctx_init = NULL;
    unsigned char Ai[EVP_MAX_MD_SIZE];
    size_t Ai_len;
    int ret = 0;

    chunk = EVP_MD_size(md);
    if (!ossl_assert(chunk > 0))
        goto err;

    ctx = EVP_MAC_CTX_new_id(EVP_MAC_HMAC);
    ctx_tmp = EVP_MAC_CTX_new_id(EVP_MAC_HMAC);
    ctx_Ai = EVP_MAC_CTX_new_id(EVP_MAC_HMAC);
    ctx_init = EVP_MAC_CTX_new_id(EVP_MAC_HMAC);
    if (ctx == NULL || ctx_tmp == NULL || ctx_init == NULL)
    if (ctx == NULL || ctx_Ai == NULL || ctx_init == NULL)
        goto err;
    if (EVP_MAC_ctrl(ctx_init, EVP_MAC_CTRL_SET_FLAGS, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) != 1)
        goto err;
@@ -196,59 +250,85 @@ static int tls1_prf_P_hash(const EVP_MD *md,
        goto err;
    if (!EVP_MAC_init(ctx_init))
        goto err;
    if (!EVP_MAC_CTX_copy(ctx, ctx_init))
    chunk = EVP_MAC_size(ctx_init);
    if (chunk == 0)
        goto err;
    if (seed != NULL && !EVP_MAC_update(ctx, seed, seed_len))
    /* A(0) = seed */
    if (!EVP_MAC_CTX_copy(ctx_Ai, ctx_init))
        goto err;
    if (!EVP_MAC_final(ctx, A1, &A1_len))
    if (seed != NULL && !EVP_MAC_update(ctx_Ai, seed, seed_len))
        goto err;

    for (;;) {
        /* Reinit mac contexts */
        /* calc: A(i) = HMAC_<hash>(secret, A(i-1)) */
        if (!EVP_MAC_final(ctx_Ai, Ai, &Ai_len))
            goto err;

        /* calc next chunk: HMAC_<hash>(secret, A(i) + seed) */
        if (!EVP_MAC_CTX_copy(ctx, ctx_init))
            goto err;
        if (!EVP_MAC_update(ctx, A1, A1_len))
        if (!EVP_MAC_update(ctx, Ai, Ai_len))
            goto err;
        if (olen > (size_t)chunk && !EVP_MAC_CTX_copy(ctx_tmp, ctx))
        /* save state for calculating next A(i) value */
        if (olen > chunk && !EVP_MAC_CTX_copy(ctx_Ai, ctx))
            goto err;
        if (seed != NULL && !EVP_MAC_update(ctx, seed, seed_len))
            goto err;

        if (olen > (size_t)chunk) {
            size_t mac_len;
            if (!EVP_MAC_final(ctx, out, &mac_len))
                goto err;
            out += mac_len;
            olen -= mac_len;
            /* calc the next A1 value */
            if (!EVP_MAC_final(ctx_tmp, A1, &A1_len))
                goto err;
        } else {                /* last one */

            if (!EVP_MAC_final(ctx, A1, &A1_len))
        if (olen <= chunk) {
            /* last chunk - use Ai as temp bounce buffer */
            if (!EVP_MAC_final(ctx, Ai, &Ai_len))
                goto err;
            memcpy(out, A1, olen);
            memcpy(out, Ai, olen);
            break;
        }
        if (!EVP_MAC_final(ctx, out, NULL))
            goto err;
        out += chunk;
        olen -= chunk;
    }
    ret = 1;
 err:
    EVP_MAC_CTX_free(ctx);
    EVP_MAC_CTX_free(ctx_tmp);
    EVP_MAC_CTX_free(ctx_Ai);
    EVP_MAC_CTX_free(ctx_init);
    OPENSSL_cleanse(A1, sizeof(A1));
    OPENSSL_cleanse(Ai, sizeof(Ai));
    return ret;
}

/*
 * Refer to "The TLS Protocol Version 1.0" Section 5
 * (https://tools.ietf.org/html/rfc2246#section-5) and
 * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
 * (https://tools.ietf.org/html/rfc5246#section-5).
 *
 * For TLS v1.0 and TLS v1.1:
 *
 *   PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
 *                              P_SHA-1(S2, label + seed)
 *
 * S1 is taken from the first half of the secret, S2 from the second half.
 *
 *   L_S = length in bytes of secret;
 *   L_S1 = L_S2 = ceil(L_S / 2);
 *
 * For TLS v1.2:
 *
 *   PRF(secret, label, seed) = P_<hash>(secret, label + seed)
 */
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 (EVP_MD_type(md) == NID_md5_sha1) {
        /* TLS v1.0 and TLS v1.1 */
        size_t i;
        unsigned char *tmp;
        if (!tls1_prf_P_hash(EVP_md5(), sec, slen/2 + (slen & 1),
        /* calc: L_S1 = L_S2 = ceil(L_S / 2) */
        size_t L_S1 = (slen + 1) / 2;
        size_t L_S2 = L_S1;

        if (!tls1_prf_P_hash(EVP_md5(), sec, L_S1,
                             seed, seed_len, out, olen))
            return 0;

@@ -256,7 +336,7 @@ static int tls1_prf_alg(const EVP_MD *md,
            KDFerr(KDF_F_TLS1_PRF_ALG, ERR_R_MALLOC_FAILURE);
            return 0;
        }
        if (!tls1_prf_P_hash(EVP_sha1(), sec + slen/2, slen/2 + (slen & 1),
        if (!tls1_prf_P_hash(EVP_sha1(), sec + slen - L_S2, L_S2,
                             seed, seed_len, tmp, olen)) {
            OPENSSL_clear_free(tmp, olen);
            return 0;
@@ -266,6 +346,8 @@ static int tls1_prf_alg(const EVP_MD *md,
        OPENSSL_clear_free(tmp, olen);
        return 1;
    }

    /* TLS v1.2 */
    if (!tls1_prf_P_hash(md, sec, slen, seed, seed_len, out, olen))
        return 0;