Skip to content

Commit

Permalink
gate libcrypto prf with > OpenSSL 1.0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed May 23, 2023
1 parent ab20222 commit 21b77d2
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 9 deletions.
5 changes: 5 additions & 0 deletions crypto/s2n_libcrypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,8 @@ unsigned long s2n_get_openssl_version(void)
{
return OPENSSL_VERSION_NUMBER;
}

bool s2n_libcrypto_supports_tls_prf()
{
return S2N_LIBCRYPTO_SUPPORTS_TLS_PRF;
}
8 changes: 8 additions & 0 deletions crypto/s2n_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@
bool s2n_libcrypto_is_awslc();
bool s2n_libcrypto_is_boringssl();
bool s2n_libcrypto_is_libressl();

#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) && !defined(LIBRESSL_VERSION_NUMBER)
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 1
#else
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 0
#endif

bool s2n_libcrypto_supports_tls_prf();
60 changes: 60 additions & 0 deletions tests/unit/s2n_tls_prf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
/* To gain access to handshake_read and handshake_write */
#include "tls/s2n_handshake_io.c"

#define TEST_BLOB_SIZE 64

/*
* Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0
*
Expand Down Expand Up @@ -310,5 +312,63 @@ int main(int argc, char **argv)
};
};

/* s2n_prf tests */
{
s2n_stack_blob(secret, TEST_BLOB_SIZE, TEST_BLOB_SIZE);
s2n_stack_blob(label, TEST_BLOB_SIZE, TEST_BLOB_SIZE);
s2n_stack_blob(seed_a, TEST_BLOB_SIZE, TEST_BLOB_SIZE);
s2n_stack_blob(seed_b, TEST_BLOB_SIZE, TEST_BLOB_SIZE);
s2n_stack_blob(seed_c, TEST_BLOB_SIZE, TEST_BLOB_SIZE);
s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE);

/* Safety */
{
DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free);

EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), S2N_ERR_NULL);

/* seed_a is required */
EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), S2N_ERR_PRF_INVALID_SEED);

/* seed_b and seed_c are optional */
EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out));

/* seed_b is required if seed_c is provided */
EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), S2N_ERR_PRF_INVALID_SEED);

/* seed_c is optional */
EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out));
}

/* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */
if (!s2n_is_in_fips_mode()) {
DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free);

uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 };
EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0);

EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out));

/* The custom PRF implementation should modify the digest fields in the prf_space */
EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0);
}

/* The libcrypto PRF implementation is used when s2n-tls is linked with AWSLC-FIPS */
if (s2n_libcrypto_is_awslc() && s2n_is_in_fips_mode()) {
DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free);

uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 };
EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0);

EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out));

/* The libcrypto PRF implementation will not modify the digest fields in the prf_space */
EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0);
}
}

END_TEST();
}
33 changes: 24 additions & 9 deletions tls/s2n_prf.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include "tls/s2n_prf.h"

#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
/* BoringSSL and AWSLC expose the CRYPTO_tls1_prf API via a private header. The function is
* forward-declared so it's accessible.
*/
int CRYPTO_tls1_prf(const EVP_MD *digest,
uint8_t *out, size_t out_len,
const uint8_t *secret, size_t secret_len,
Expand Down Expand Up @@ -501,7 +504,13 @@ static S2N_RESULT s2n_custom_prf(struct s2n_connection *conn, struct s2n_blob *s
return S2N_RESULT_OK;
}

#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
#if !S2N_LIBCRYPTO_SUPPORTS_TLS_PRF
static S2N_RESULT s2n_libcrypto_prf(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_BAIL(S2N_ERR_UNIMPLEMENTED);
}
#elif defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
static S2N_RESULT s2n_libcrypto_prf(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)
{
Expand All @@ -518,9 +527,9 @@ static S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob
size_t seed2_len = 0;

if (seed_b != NULL && seed_c != NULL) {
/* The AWSLC TLS PRF implementation doesn't support providing more than a label and two
* seeds. If three seeds were provided, pass in the third seed by concatenating it with the
* second seed.
/* The AWSLC/BoringSSL TLS PRF implementation doesn't support providing more than a label
* and two seeds. If three seeds were provided, pass in the third seed by concatenating it
* with the second seed.
*/
RESULT_GUARD_POSIX(s2n_alloc(&seed2_dyn, seed_b->size + seed_c->size));
RESULT_CHECKED_MEMCPY(seed2_dyn.data, seed_b->data, seed_b->size);
Expand All @@ -540,7 +549,7 @@ static S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob
#else
DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX*, EVP_PKEY_CTX_free);

static EVP_MD* s2n_unsafe_evp_md_get_non_const(const EVP_MD *md)
static EVP_MD* s2n_unsafe_get_non_const_evp_md(const EVP_MD *md)
{
PTR_ENSURE_REF(md);

Expand Down Expand Up @@ -571,7 +580,12 @@ static S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob
RESULT_ENSURE_REF(pctx);

RESULT_GUARD_OSSL(EVP_PKEY_derive_init(pctx), S2N_ERR_PRF_DERIVE);
RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_tls1_prf_md(pctx, s2n_unsafe_evp_md_get_non_const(md)), S2N_ERR_PRF_DERIVE);

/* The EVP_PKEY_CTX_set_tls1_prf_md macro casts the md EVP_MD argument to a void *, discarding
* the const qualifier. The compiler warnings are turned off before this cast.
*/
RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_tls1_prf_md(pctx, s2n_unsafe_get_non_const_evp_md(md)), S2N_ERR_PRF_DERIVE);

RESULT_GUARD_OSSL(EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, secret->data, secret->size), S2N_ERR_PRF_DERIVE);
RESULT_GUARD_OSSL(EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, label->data, label->size), S2N_ERR_PRF_DERIVE);
RESULT_GUARD_OSSL(EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed_a->data, seed_a->size), S2N_ERR_PRF_DERIVE);
Expand All @@ -590,7 +604,7 @@ static S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob
}
#endif /* defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) */

static int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a,
int s2n_prf(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)
{
POSIX_ENSURE_REF(conn);
Expand All @@ -612,9 +626,10 @@ static int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct
}

/* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the
* FIPS-validated libcrypto implementation is used instead.
* FIPS-validated libcrypto implementation is used instead, if the libcrypto implements the
* TLS PRF.
*/
if (true) {
if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_tls_prf()) {
POSIX_GUARD_RESULT(s2n_libcrypto_prf(conn, secret, label, seed_a, seed_b, seed_c, out));
return S2N_SUCCESS;
}
Expand Down
2 changes: 2 additions & 0 deletions tls/s2n_prf.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ S2N_RESULT s2n_prf_new(struct s2n_connection *conn);
S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn);
S2N_RESULT s2n_prf_free(struct s2n_connection *conn);

int s2n_prf(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);
int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
int s2n_tls_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
int s2n_hybrid_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
Expand Down

0 comments on commit 21b77d2

Please sign in to comment.