S2N_RESULT s2n_prf_libcrypto()

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;
}