From 037ed4a124bcc9e579493a48de05952f66047f5b Mon Sep 17 00:00:00 2001 From: Will Childs-Klein Date: Fri, 24 Feb 2023 15:26:33 -0500 Subject: [PATCH] Migrate Kyber 512 to EVP KEM API New usage is based on [the KEM API design document][1] and [header][2]. Now that we're on a stable KEM API, we remove the S2N_AWSLC_KYBER_UNSTABLE build flag and always use the linked libcrypto's Kyber implementation if available. This flag wasn't previously specified in any of our CI scripts, meaning that AWS-LC-backed kyber was previously uncovered in s2n's CI. This commit ensures that coverage and updates the PQ KEM unit test to asserts that if (non-FIPS) AWS-LC is used as the backing libcrypto, it has the new Kyber 512 KEM API available. [1]: https://github.com/aws/aws-lc/blob/main/crypto/kem/README.md [2]: https://github.com/aws/aws-lc/blob/92c56fbc15f9bb43c4ff062c6c02f7991fd417f6/include/openssl/evp.h#L880 Check for other pre-processor symbol from AWS-LC --- CMakeLists.txt | 9 +++-- pq-crypto/s2n_kyber_512_evp.c | 36 +++++++++++-------- s2n.mk | 6 ++++ .../{evp_kyber_512.c => evp_kem_kyber_512.c} | 13 +++++-- tests/unit/s2n_pq_kem_test.c | 17 +++++++++ 5 files changed, 59 insertions(+), 22 deletions(-) rename tests/features/{evp_kyber_512.c => evp_kem_kyber_512.c} (67%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6cd2f9e89e..420711f9b26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ option(S2N_NO_PQ "Disables all Post Quantum Crypto code. You likely want this for older compilers or uncommon platforms." OFF) option(S2N_NO_PQ_ASM "Turns off the ASM for PQ Crypto even if it's available for the toolchain. You likely want this on older compilers." OFF) -option(S2N_AWSLC_KYBER_UNSTABLE "Prefer the AWS-LC provided PQ implementation." OFF) option(SEARCH_LIBCRYPTO "Set this if you want to let S2N search libcrypto for you, otherwise a crypto target needs to be defined." ON) option(UNSAFE_TREAT_WARNINGS_AS_ERRORS "Compiler warnings are treated as errors. Warnings may @@ -445,16 +444,16 @@ if (LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX) target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX) endif() -# Determine if Kyber512 implementation from AWS-LC is available +# Determine if KEM Kyber512 implementation from AWS-LC is available try_compile( - LIBCRYPTO_SUPPORTS_EVP_KYBER_512 + LIBCRYPTO_SUPPORTS_EVP_KEM_KYBER_512 ${CMAKE_BINARY_DIR} - SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/evp_kyber_512.c" + SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/evp_kem_kyber_512.c" LINK_LIBRARIES ${LINK_LIB} ${OS_LIBS} COMPILE_DEFINITIONS "-Werror" ) -if(S2N_AWSLC_KYBER_UNSTABLE AND LIBCRYPTO_SUPPORTS_EVP_KYBER_512) +if(LIBCRYPTO_SUPPORTS_EVP_KEM_KYBER_512) target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_LIBCRYPTO_SUPPORTS_KYBER512) endif() diff --git a/pq-crypto/s2n_kyber_512_evp.c b/pq-crypto/s2n_kyber_512_evp.c index 3d8411d6de8..7a389162ed0 100644 --- a/pq-crypto/s2n_kyber_512_evp.c +++ b/pq-crypto/s2n_kyber_512_evp.c @@ -20,21 +20,27 @@ #include "error/s2n_errno.h" #include "tls/s2n_kem.h" +#include "utils/s2n_safety.h" #include "utils/s2n_safety_macros.h" -#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER512) -int s2n_kyber_512_evp_generate_keypair(uint8_t *public_key, uint8_t *private_key) { - EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_KYBER512, NULL); +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER512) && !defined(S2N_NO_PQ) + +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY *, EVP_PKEY_free); +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); + +int s2n_kyber_512_evp_generate_keypair(uint8_t *public_key, uint8_t *secret_key) { + DEFER_CLEANUP(EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, NULL), EVP_PKEY_CTX_free_pointer); POSIX_GUARD_PTR(kyber_pkey_ctx); + POSIX_GUARD_OSSL(EVP_PKEY_CTX_kem_set_params(kyber_pkey_ctx, NID_KYBER512_R3), S2N_ERR_PQ_CRYPTO); POSIX_GUARD_OSSL(EVP_PKEY_keygen_init(kyber_pkey_ctx), S2N_ERR_PQ_CRYPTO); - EVP_PKEY *kyber_pkey = NULL; + DEFER_CLEANUP(EVP_PKEY *kyber_pkey = NULL, EVP_PKEY_free_pointer); POSIX_GUARD_OSSL(EVP_PKEY_keygen(kyber_pkey_ctx, &kyber_pkey), S2N_ERR_PQ_CRYPTO); size_t public_key_size = S2N_KYBER_512_R3_PUBLIC_KEY_BYTES; - size_t private_key_size = S2N_KYBER_512_R3_SECRET_KEY_BYTES; + size_t secret_key_size = S2N_KYBER_512_R3_SECRET_KEY_BYTES; POSIX_GUARD_OSSL(EVP_PKEY_get_raw_public_key(kyber_pkey, public_key, &public_key_size), S2N_ERR_PQ_CRYPTO); - POSIX_GUARD_OSSL(EVP_PKEY_get_raw_private_key(kyber_pkey, private_key, &private_key_size), S2N_ERR_PQ_CRYPTO); + POSIX_GUARD_OSSL(EVP_PKEY_get_raw_private_key(kyber_pkey, secret_key, &secret_key_size), S2N_ERR_PQ_CRYPTO); return S2N_SUCCESS; } @@ -42,10 +48,10 @@ int s2n_kyber_512_evp_generate_keypair(uint8_t *public_key, uint8_t *private_key int s2n_kyber_512_evp_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret, const uint8_t *public_key) { size_t public_key_size = S2N_KYBER_512_R3_PUBLIC_KEY_BYTES; - EVP_PKEY *kyber_pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_KYBER512, NULL, public_key, public_key_size); + DEFER_CLEANUP(EVP_PKEY *kyber_pkey = EVP_PKEY_kem_new_raw_public_key(NID_KYBER512_R3, public_key, public_key_size), EVP_PKEY_free_pointer); POSIX_GUARD_PTR(kyber_pkey); - EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new(kyber_pkey, NULL); + DEFER_CLEANUP(EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new(kyber_pkey, NULL), EVP_PKEY_CTX_free_pointer); POSIX_GUARD_PTR(kyber_pkey_ctx); size_t cipher_text_size = S2N_KYBER_512_R3_CIPHERTEXT_BYTES; @@ -56,21 +62,21 @@ int s2n_kyber_512_evp_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret, } int s2n_kyber_512_evp_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext, - const uint8_t *private_key) { - size_t private_key_size = S2N_KYBER_512_R3_SECRET_KEY_BYTES; - EVP_PKEY *kyber_pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_KYBER512, NULL, private_key, private_key_size); + const uint8_t *secret_key) { + size_t secret_key_size = S2N_KYBER_512_R3_SECRET_KEY_BYTES; + DEFER_CLEANUP(EVP_PKEY *kyber_pkey = EVP_PKEY_kem_new_raw_secret_key(NID_KYBER512_R3, secret_key, secret_key_size), EVP_PKEY_free_pointer); POSIX_GUARD_PTR(kyber_pkey); - EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new(kyber_pkey, NULL); + DEFER_CLEANUP(EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new(kyber_pkey, NULL), EVP_PKEY_CTX_free_pointer); POSIX_GUARD_PTR(kyber_pkey_ctx); size_t shared_secret_size = S2N_KYBER_512_R3_SHARED_SECRET_BYTES; POSIX_GUARD_OSSL(EVP_PKEY_decapsulate(kyber_pkey_ctx, shared_secret, &shared_secret_size, (uint8_t *) ciphertext, - S2N_KYBER_512_R3_CIPHERTEXT_BYTES), S2N_ERR_PQ_CRYPTO); + S2N_KYBER_512_R3_CIPHERTEXT_BYTES), S2N_ERR_PQ_CRYPTO); return S2N_SUCCESS; } #else -int s2n_kyber_512_evp_generate_keypair(OUT uint8_t *public_key, OUT uint8_t *private_key) { +int s2n_kyber_512_evp_generate_keypair(OUT uint8_t *public_key, OUT uint8_t *secret_key) { POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); } @@ -80,7 +86,7 @@ int s2n_kyber_512_evp_encapsulate(OUT uint8_t *ciphertext, OUT uint8_t *shared_s } int s2n_kyber_512_evp_decapsulate(OUT uint8_t *shared_secret, IN const uint8_t *ciphertext, - IN const uint8_t *private_key) { + IN const uint8_t *secret_key) { POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); } #endif diff --git a/s2n.mk b/s2n.mk index c75adc3da6c..b91148b4837 100644 --- a/s2n.mk +++ b/s2n.mk @@ -231,6 +231,12 @@ ifeq ($(TRY_EVP_MD_CTX_SET_PKEY_CTX), 0) DEFAULT_CFLAGS += -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX endif +# Determine if the Kyber 512 KEM API is available in libcrypto +TRY_LIBCRYPTO_SUPPORTS_KYBER512 := $(call try_compile,$(S2N_ROOT)/tests/features/evp_kem_kyber_512.c) +ifeq ($(TRY_LIBCRYPTO_SUPPORTS_KYBER512), 0) + DEFAULT_CFLAGS += -DS2N_LIBCRYPTO_SUPPORTS_KYBER512 +endif + # Determine if madvise() is available TRY_COMPILE_MADVISE := $(call try_compile,$(S2N_ROOT)/tests/features/madvise.c) ifeq ($(TRY_COMPILE_MADVISE), 0) diff --git a/tests/features/evp_kyber_512.c b/tests/features/evp_kem_kyber_512.c similarity index 67% rename from tests/features/evp_kyber_512.c rename to tests/features/evp_kem_kyber_512.c index cad441e11fb..6bf513351ca 100644 --- a/tests/features/evp_kyber_512.c +++ b/tests/features/evp_kem_kyber_512.c @@ -14,8 +14,17 @@ */ #include +#include int main() { - EVP_PKEY_CTX *kyber_pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_KYBER512, NULL); - return 0; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, NULL); + if (ctx == NULL) { + return 1; + } + if (!EVP_PKEY_CTX_kem_set_params(ctx, NID_KYBER512_R3)) { + EVP_PKEY_CTX_free(ctx); + return 1; + } + EVP_PKEY_CTX_free(ctx); + return 0; } diff --git a/tests/unit/s2n_pq_kem_test.c b/tests/unit/s2n_pq_kem_test.c index 3b8433ef752..47c5821c856 100644 --- a/tests/unit/s2n_pq_kem_test.c +++ b/tests/unit/s2n_pq_kem_test.c @@ -13,6 +13,10 @@ * permissions and limitations under the License. */ +#include + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_openssl.h" #include "pq-crypto/s2n_pq.h" #include "s2n_test.h" #include "tests/testlib/s2n_testlib.h" @@ -50,6 +54,19 @@ int main() { BEGIN_TEST(); +#if defined(OPENSSL_IS_AWSLC) && defined(AWS_LC_API_VERSION) + const unsigned long lc_vers = awslc_api_version_num(); +#else + const unsigned long lc_vers = SSLeay(); +#endif + + /* If using non-FIPS AWS-LC >= v1.4.0 (API vers. 20), expect Kyber512 KEM from AWS-LC */ + if (s2n_libcrypto_is_awslc() && lc_vers >= 20 && !s2n_libcrypto_is_fips()) { + EXPECT_TRUE(s2n_libcrypto_supports_kyber_512()); + } else { + EXPECT_FALSE(s2n_libcrypto_supports_kyber_512()); + } + for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { const struct s2n_kem_test_vector vector = test_vectors[i]; const struct s2n_kem *kem = vector.kem;