From: David Makepeace Date: Thu, 23 May 2019 04:36:24 +0000 (+1000) Subject: Cleaned up tls1_prf and added comments. X-Git-Tag: openssl-3.0.0-alpha1~2040 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=0f52d9ed7ed9f9a447131b79f252b8254e69781f;p=oweals%2Fopenssl.git Cleaned up tls1_prf and added comments. Reviewed-by: Richard Levitte Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/8987) --- diff --git a/crypto/kdf/tls1_prf.c b/crypto/kdf/tls1_prf.c index 913af2905b..c39cf204f7 100644 --- a/crypto/kdf/tls1_prf.c +++ b/crypto/kdf/tls1_prf.c @@ -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_, 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_(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_ is an expansion function that uses a single hash function to expand + * a secret and seed into an arbitrary quantity of output: + * + * P_(secret, seed) = HMAC_(secret, A(1) + seed) + + * HMAC_(secret, A(2) + seed) + + * HMAC_(secret, A(3) + seed) + ... + * + * where + indicates concatenation. P_ 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_(secret, A(i-1)) + */ #include #include #include @@ -82,7 +120,7 @@ static int kdf_tls1_prf_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args) if (impl->sec == NULL) return 0; - impl->seclen = len; + impl->seclen = len; return 1; case EVP_KDF_CTRL_RESET_TLS_SEED: @@ -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_ is an expansion function that uses a single hash function to expand + * a secret and seed into an arbitrary quantity of output: + * + * P_(secret, seed) = HMAC_(secret, A(1) + seed) + + * HMAC_(secret, A(2) + seed) + + * HMAC_(secret, A(3) + seed) + ... + * + * where + indicates concatenation. P_ 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_(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_(secret, A(i-1)) */ + if (!EVP_MAC_final(ctx_Ai, Ai, &Ai_len)) + goto err; + + /* calc next chunk: HMAC_(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)) + if (olen <= chunk) { + /* last chunk - use Ai as temp bounce buffer */ + if (!EVP_MAC_final(ctx, Ai, &Ai_len)) goto err; - } else { /* last one */ - - if (!EVP_MAC_final(ctx, A1, &A1_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_(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;