From 302ce4a2f6658348a23277841c2163fb0292fbfd Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Wed, 26 Feb 2025 14:42:28 -0800 Subject: [PATCH] refactor: add libcrypto PRF impl for openssl-3.0-fips --- codebuild/spec/buildspec_openssl3fips.yml | 1 + crypto/s2n_prf_libcrypto.c | 192 +++++++++++++ crypto/s2n_prf_libcrypto.h | 24 ++ tests/unit/s2n_tls_hybrid_prf_test.c | 10 + tests/unit/s2n_tls_prf_test.c | 312 +++++++++++++++------- tls/s2n_prf.c | 90 +------ 6 files changed, 450 insertions(+), 179 deletions(-) create mode 100644 crypto/s2n_prf_libcrypto.c create mode 100644 crypto/s2n_prf_libcrypto.h diff --git a/codebuild/spec/buildspec_openssl3fips.yml b/codebuild/spec/buildspec_openssl3fips.yml index 42713db2f5c..d89aca1271c 100644 --- a/codebuild/spec/buildspec_openssl3fips.yml +++ b/codebuild/spec/buildspec_openssl3fips.yml @@ -38,3 +38,4 @@ phases: - make -C build test -- ARGS="-R 's2n_build_test|s2n_fips_test'" - make -C build test -- ARGS="-R 's2n_hash_test|s2n_hash_all_algs_test|s2n_openssl_test|s2n_init_test'" - make -C build test -- ARGS="-R 's2n_evp_signing_test'" + - make -C build test -- ARGS="-R 's2n_tls_prf_test|s2n_tls_hybrid_prf_test'" diff --git a/crypto/s2n_prf_libcrypto.c b/crypto/s2n_prf_libcrypto.c new file mode 100644 index 00000000000..0e74372d503 --- /dev/null +++ b/crypto/s2n_prf_libcrypto.c @@ -0,0 +1,192 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "crypto/s2n_prf_libcrypto.h" + +#include "crypto/s2n_hash.h" +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_safety.h" + +#if defined(OPENSSL_IS_AWSLC) + +/* The AWSLC TLS PRF API is exported in all AWSLC versions. However, in the AWSLC FIPS branch, this + * API is defined in a private header: + * https://github.com/aws/aws-lc/blob/d251b365b73a6e6acff6ee634aa8f077f23cdea4/crypto/fipsmodule/tls/internal.h#L27 + * + * AWSLC has committed to this API definition, and the API has been added to a public header in the + * main branch: https://github.com/aws/aws-lc/pull/1033. As such, this API is forward-declared in + * order to make it accessible to s2n-tls when linked to AWSLC-FIPS. + */ +int CRYPTO_tls1_prf(const EVP_MD *digest, + uint8_t *out, size_t out_len, + const uint8_t *secret, size_t secret_len, + const char *label, size_t label_len, + const uint8_t *seed1, size_t seed1_len, + const uint8_t *seed2, size_t seed2_len); + +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) +{ + const EVP_MD *digest = NULL; + if (conn->actual_protocol_version < S2N_TLS12) { + /* md5_sha1 is a digest that indicates both MD5 and SHA1 should be used in the PRF calculation. + * This is needed for pre-TLS12 PRFs. + */ + digest = EVP_md5_sha1(); + } else { + RESULT_GUARD(s2n_hmac_md_from_alg(conn->secure->cipher_suite->prf_alg, &digest)); + } + RESULT_ENSURE_REF(digest); + + DEFER_CLEANUP(struct s2n_stuffer seed_b_stuffer = { 0 }, s2n_stuffer_free); + size_t seed_b_len = 0; + uint8_t *seed_b_data = NULL; + + if (seed_b != NULL) { + struct s2n_blob seed_b_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&seed_b_blob, seed_b->data, seed_b->size)); + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&seed_b_stuffer, &seed_b_blob)); + + if (seed_c != NULL) { + /* The AWSLC TLS PRF implementation only provides two seed arguments. If three seeds + * were provided, pass in the third seed by concatenating it with the second seed. + */ + RESULT_GUARD_POSIX(s2n_stuffer_alloc(&seed_b_stuffer, seed_b->size + seed_c->size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_b->data, seed_b->size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_c->data, seed_c->size)); + } + + seed_b_len = s2n_stuffer_data_available(&seed_b_stuffer); + seed_b_data = s2n_stuffer_raw_read(&seed_b_stuffer, seed_b_len); + RESULT_ENSURE_REF(seed_b_data); + } + + RESULT_GUARD_OSSL(CRYPTO_tls1_prf(digest, + out->data, out->size, + secret->data, secret->size, + (const char *) label->data, label->size, + seed_a->data, seed_a->size, + seed_b_data, seed_b_len), + S2N_ERR_PRF_DERIVE); + + return S2N_RESULT_OK; +} + +#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0) + + #include + #include + + #define OSSL_PARAM_BLOB(id, blob) \ + OSSL_PARAM_octet_string(id, blob->data, blob->size) + #define OSSL_PARAM_STR(id, cstr) \ + OSSL_PARAM_utf8_string(id, cstr, 0) + +DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF_CTX *, EVP_KDF_CTX_free); +DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF *, EVP_KDF_free); + +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 = NULL; + + 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(digest, S2N_ERR_PRF_INVALID_ALGORITHM); + digest_name = EVP_MD_get0_name(digest); + } + + /* 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. + */ + OSSL_PARAM_STR(OSSL_KDF_PARAM_PROPERTIES, (void *) (uintptr_t) fetch_properties), + OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), + OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SECRET, secret), + /* "TLS1-PRF" handles the label like just another seed */ + OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, label), + OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_a), + OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_b), + 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; +} + +#else + +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_BAIL(S2N_ERR_FIPS_MODE_UNSUPPORTED); +} + +#endif diff --git a/crypto/s2n_prf_libcrypto.h b/crypto/s2n_prf_libcrypto.h new file mode 100644 index 00000000000..1cb14fd3825 --- /dev/null +++ b/crypto/s2n_prf_libcrypto.h @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#pragma once + +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +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); diff --git a/tests/unit/s2n_tls_hybrid_prf_test.c b/tests/unit/s2n_tls_hybrid_prf_test.c index acdc4dfed65..f0996091534 100644 --- a/tests/unit/s2n_tls_hybrid_prf_test.c +++ b/tests/unit/s2n_tls_hybrid_prf_test.c @@ -17,6 +17,7 @@ #include #include "api/s2n.h" +#include "crypto/s2n_pq.h" #include "s2n_test.h" #include "stuffer/s2n_stuffer.h" #include "tests/testlib/s2n_nist_kats.h" @@ -40,6 +41,15 @@ int main(int argc, char **argv) BEGIN_TEST(); EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + if (!s2n_pq_is_enabled()) { + /* The hybrid PRF sets a seed too large for the openssl PRF, + * but s2n-tls doesn't support PQ with openssl anyway. + * + * Only run this test in environments where PQ is possible. + */ + END_TEST(); + } + FILE *kat_file = fopen(KAT_FILE_NAME, "r"); EXPECT_NOT_NULL(kat_file); diff --git a/tests/unit/s2n_tls_prf_test.c b/tests/unit/s2n_tls_prf_test.c index d8ec146b047..ac97d6423c2 100644 --- a/tests/unit/s2n_tls_prf_test.c +++ b/tests/unit/s2n_tls_prf_test.c @@ -26,7 +26,6 @@ #define TEST_BLOB_SIZE 64 -bool s2n_libcrypto_supports_tls_prf(); 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); @@ -58,11 +57,215 @@ int main(int argc, char **argv) S2N_BLOB_FROM_HEX(master_secret_in, "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); - struct s2n_connection *conn = NULL; + /* s2n_prf tests */ + { + uint8_t secret_bytes[TEST_BLOB_SIZE] = "secret"; + struct s2n_blob secret = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&secret, secret_bytes, sizeof(secret_bytes))); + + uint8_t label_bytes[TEST_BLOB_SIZE] = "label"; + struct s2n_blob label = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&label, label_bytes, sizeof(label_bytes))); + + uint8_t seed_a_bytes[TEST_BLOB_SIZE] = "seed a"; + struct s2n_blob seed_a = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_a, seed_a_bytes, sizeof(seed_a_bytes))); + + uint8_t seed_b_bytes[TEST_BLOB_SIZE] = "seed b"; + struct s2n_blob seed_b = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_b, seed_b_bytes, sizeof(seed_b_bytes))); + + uint8_t seed_c_bytes[TEST_BLOB_SIZE] = "seed c"; + struct s2n_blob seed_c = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_c, seed_c_bytes, sizeof(seed_c_bytes))); + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + + 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)); + } + + /* Test: secret and label */ + { + struct s2n_blob empty = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "d6 4d 7e e1 f3 ca ff a0 d4 7e 84 18 ff 64 97 d8" + "56 4d d1 99 5e ea 53 0d 29 b0 42 68 89 45 f3 58 86" + "f5 4b 12 ff b3 83 87 80 d5 ba 7d f6 26 10 a5 cc 39" + "d9 e7 37 6e eb 28 8e 21 29 53 13 54 f5 f6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &empty, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, and seed_a */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4e f2 13 65 22 07 f1 e5 86 dc 03 aa 60 a2 c9 5c" + "ab 05 5e 43 f0 c8 3a 70 6c f4 be 21 44 9e b8 45 70" + "d9 e8 e1 83 3b f7 65 dd e9 d1 ea ae 14 f7 73 c5 47" + "57 da 0a a8 5f cc 75 ff b2 9e 3d 02 01 e9"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, seed_a, and seed_b */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "45 0b b0 da 24 d5 23 05 b6 00 f9 1c 71 6f ca 40" + "54 ba 1b 6c 91 43 77 9c 0f 6f 2c e6 e4 24 30 ee b3" + "fc 83 85 3a 02 60 1e 37 2c 2c ee d3 b1 8a 6a cc 75" + "d9 84 87 42 d8 b6 a9 9c 1b f8 72 8c 6b 71"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: all inputs */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4f b6 e2 ac d0 68 dc 55 70 43 ab 98 f6 23 a8 93" + "6b f2 0f 48 1c 74 50 7e a0 f2 ef 49 53 a6 b0 56 84" + "fe 2e a9 76 31 50 44 f7 8d e7 3d 52 97 ce 36 82 a8" + "d7 27 59 f7 7a 73 19 06 6c 0a 1a df 68 c6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: cipher specific digest */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + const char *expected_hex = + "5b 54 71 ec b9 8a 49 ce f1 6a d9 31 cf c9 76 be" + "5a e6 25 bd a3 45 69 45 8c 6c 1a a1 98 06 d9 0d cc" + "c8 cd 7c aa d7 e2 59 25 4b 36 ff f7 01 a6 7e 89 22" + "0f bd 06 15 bf 9d 7e d1 53 45 a3 1b 36 da"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: large seeds fail for openssl-3.0-fips */ + if (s2n_libcrypto_is_openssl_fips()) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + DEFER_CLEANUP(struct s2n_blob small_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&small_seed, 30)); + + DEFER_CLEANUP(struct s2n_blob medium_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&medium_seed, 300)); + + DEFER_CLEANUP(struct s2n_blob large_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&large_seed, 3000)); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &small_seed, &small_seed, &small_seed, &large_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &medium_seed, &medium_seed, &medium_seed, &medium_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_SUCCESS(s2n_prf(conn, &secret, + &small_seed, &small_seed, &small_seed, &small_seed, &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); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + 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 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); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + 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); + } + } /* s2n_tls_prf_master_secret */ { - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); /* Check the most common PRF */ conn->actual_protocol_version = S2N_TLS11; @@ -75,8 +278,6 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; /* s2n_tls_prf_extended_master_secret */ @@ -96,7 +297,9 @@ int main(int argc, char **argv) "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" "9ca768fe389eab3b53c98d8ccd830b06"); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); conn->actual_protocol_version = S2N_TLS12; conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; @@ -112,13 +315,13 @@ int main(int argc, char **argv) */ EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; /* s2n_prf_calculate_master_secret */ { - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; @@ -151,8 +354,6 @@ int main(int argc, char **argv) /* Extended master secret calculated is different than the master secret calculated */ EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. @@ -273,25 +474,25 @@ int main(int argc, char **argv) /* PRF freed by s2n_connection_free_handshake */ { - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); EXPECT_NOT_NULL(conn->prf_space); EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); EXPECT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; /* Freed PRF restored by s2n_connection_wipe */ { - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); EXPECT_OK(s2n_prf_free(conn)); EXPECT_NULL(conn->prf_space); EXPECT_SUCCESS(s2n_connection_wipe(conn)); EXPECT_NOT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; /* PRF usable throughout connection lifecycle */ @@ -299,7 +500,9 @@ int main(int argc, char **argv) struct s2n_blob pms = { 0 }; EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); @@ -318,81 +521,8 @@ int main(int argc, char **argv) EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); }; }; - /* Ensure that the libcrypto TLS PRF API is only enabled for AWSLC */ - if (s2n_libcrypto_is_awslc()) { - EXPECT_TRUE(s2n_libcrypto_supports_tls_prf()); - } else { - EXPECT_FALSE(s2n_libcrypto_supports_tls_prf()); - } - - /* 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(); } diff --git a/tls/s2n_prf.c b/tls/s2n_prf.c index f68c0854ac6..159116999e7 100644 --- a/tls/s2n_prf.c +++ b/tls/s2n_prf.c @@ -24,6 +24,7 @@ #include "crypto/s2n_fips.h" #include "crypto/s2n_hash.h" #include "crypto/s2n_hmac.h" +#include "crypto/s2n_prf_libcrypto.h" #include "error/s2n_errno.h" #include "stuffer/s2n_stuffer.h" #include "tls/s2n_cipher_suites.h" @@ -34,12 +35,6 @@ #include "utils/s2n_mem.h" #include "utils/s2n_safety.h" -#if defined(OPENSSL_IS_AWSLC) - #define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 1 -#else - #define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 0 -#endif - /* The s2n p_hash implementation is abstracted to allow for separate implementations. * Currently the only implementation uses s2n-tls's custom HMAC implementation. */ @@ -369,15 +364,6 @@ S2N_RESULT s2n_prf_free(struct s2n_connection *conn) return S2N_RESULT_OK; } -bool s2n_libcrypto_supports_tls_prf() -{ -#if S2N_LIBCRYPTO_SUPPORTS_TLS_PRF - return true; -#else - return false; -#endif -} - S2N_RESULT s2n_prf_custom(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) { @@ -405,78 +391,6 @@ S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, return S2N_RESULT_OK; } -#if S2N_LIBCRYPTO_SUPPORTS_TLS_PRF - -/* The AWSLC TLS PRF API is exported in all AWSLC versions. However, in the AWSLC FIPS branch, this - * API is defined in a private header: - * https://github.com/aws/aws-lc/blob/d251b365b73a6e6acff6ee634aa8f077f23cdea4/crypto/fipsmodule/tls/internal.h#L27 - * - * AWSLC has committed to this API definition, and the API has been added to a public header in the - * main branch: https://github.com/aws/aws-lc/pull/1033. As such, this API is forward-declared in - * order to make it accessible to s2n-tls when linked to AWSLC-FIPS. - */ -int CRYPTO_tls1_prf(const EVP_MD *digest, - uint8_t *out, size_t out_len, - const uint8_t *secret, size_t secret_len, - const char *label, size_t label_len, - const uint8_t *seed1, size_t seed1_len, - const uint8_t *seed2, size_t seed2_len); - -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) -{ - const EVP_MD *digest = NULL; - if (conn->actual_protocol_version < S2N_TLS12) { - /* md5_sha1 is a digest that indicates both MD5 and SHA1 should be used in the PRF calculation. - * This is needed for pre-TLS12 PRFs. - */ - digest = EVP_md5_sha1(); - } else { - RESULT_GUARD(s2n_hmac_md_from_alg(conn->secure->cipher_suite->prf_alg, &digest)); - } - RESULT_ENSURE_REF(digest); - - DEFER_CLEANUP(struct s2n_stuffer seed_b_stuffer = { 0 }, s2n_stuffer_free); - size_t seed_b_len = 0; - uint8_t *seed_b_data = NULL; - - if (seed_b != NULL) { - struct s2n_blob seed_b_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&seed_b_blob, seed_b->data, seed_b->size)); - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&seed_b_stuffer, &seed_b_blob)); - - if (seed_c != NULL) { - /* The AWSLC TLS PRF implementation only provides two seed arguments. If three seeds - * were provided, pass in the third seed by concatenating it with the second seed. - */ - RESULT_GUARD_POSIX(s2n_stuffer_alloc(&seed_b_stuffer, seed_b->size + seed_c->size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_b->data, seed_b->size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_c->data, seed_c->size)); - } - - seed_b_len = s2n_stuffer_data_available(&seed_b_stuffer); - seed_b_data = s2n_stuffer_raw_read(&seed_b_stuffer, seed_b_len); - RESULT_ENSURE_REF(seed_b_data); - } - - RESULT_GUARD_OSSL(CRYPTO_tls1_prf(digest, - out->data, out->size, - secret->data, secret->size, - (const char *) label->data, label->size, - seed_a->data, seed_a->size, - seed_b_data, seed_b_len), - S2N_ERR_PRF_DERIVE); - - return S2N_RESULT_OK; -} -#else -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_BAIL(S2N_ERR_UNIMPLEMENTED); -} -#endif /* S2N_LIBCRYPTO_SUPPORTS_TLS_PRF */ - 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) { @@ -500,7 +414,7 @@ int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blo /* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. */ - if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_tls_prf()) { + if (s2n_is_in_fips_mode()) { POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out)); return S2N_SUCCESS; }