in crypto/s2n_prf_libcrypto.c [94:172]
S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c,
struct s2n_blob *out)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(secret);
RESULT_ENSURE_REF(label);
RESULT_ENSURE_REF(seed_a);
RESULT_ENSURE_REF(out);
struct s2n_blob empty_seed = { 0 };
if (!seed_b) {
seed_b = &empty_seed;
}
if (!seed_c) {
seed_c = &empty_seed;
}
/* Openssl limits the size of the seed to 1024 bytes, including the label.
* This would be an issue for TLS1.2 PQ, which uses full keyshares as seeds.
* However, s2n-tls doesn't support PQ with Openssl, so this limitation will
* never affect customers.
*
* As of this commit, EVP_KDF_derive will fail silently (without logging any
* error) if the seed is too large. This check adds visibility.
*/
uint64_t seed_total_size = label->size + seed_a->size + seed_b->size + seed_c->size;
RESULT_ENSURE(seed_total_size <= 1024, S2N_ERR_PRF_INVALID_SEED);
const char *digest_name = "MD5-SHA1";
const char *fetch_properties = "-fips";
if (conn->actual_protocol_version == S2N_TLS12) {
fetch_properties = "";
RESULT_ENSURE_REF(conn->secure);
RESULT_ENSURE_REF(conn->secure->cipher_suite);
s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg;
const EVP_MD *digest = NULL;
RESULT_GUARD(s2n_hmac_md_from_alg(prf_alg, &digest));
RESULT_ENSURE_REF(digest);
digest_name = EVP_MD_get0_name(digest);
RESULT_ENSURE_REF(digest_name);
}
/* As an optimization, we should be able to fetch and cache this EVP_KDF*
* once when s2n_init is called.
*/
DEFER_CLEANUP(EVP_KDF *prf_impl = EVP_KDF_fetch(NULL, "TLS1-PRF", fetch_properties),
EVP_KDF_free_pointer);
RESULT_ENSURE(prf_impl, S2N_ERR_PRF_INVALID_ALGORITHM);
DEFER_CLEANUP(EVP_KDF_CTX *prf_ctx = EVP_KDF_CTX_new(prf_impl),
EVP_KDF_CTX_free_pointer);
RESULT_ENSURE_REF(prf_ctx);
OSSL_PARAM params[] = {
/* Casting away the const is safe because providers are forbidden from
* modifying any OSSL_PARAM value other than return_size.
* Even the examples in the Openssl documentation cast const strings to
* non-const void pointers when setting up OSSL_PARAMs.
*/
S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_PROPERTIES, (void *) (uintptr_t) fetch_properties),
S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name),
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SECRET, secret),
/* "TLS1-PRF" handles the label like just another seed */
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, label),
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_a),
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_b),
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_c),
OSSL_PARAM_END,
};
RESULT_GUARD_OSSL(EVP_KDF_derive(prf_ctx, out->data, out->size, params),
S2N_ERR_PRF_DERIVE);
return S2N_RESULT_OK;
}