diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f2f8b6d4d2..07875479720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,9 +197,8 @@ if(CRYPTO STREQUAL "mbedtls") add_definitions(-DLIBSPDM_SM3_256_SUPPORT=0) add_definitions(-DLIBSPDM_EDDSA_ED25519_SUPPORT=0) add_definitions(-DLIBSPDM_EDDSA_ED448_SUPPORT=0) - add_definitions(-DLIBSPDM_SM2_DSA_P256_SUPPORT=0) - add_definitions(-DLIBSPDM_SM2_KEY_EXCHANGE_P256_SUPPORT=0) - add_definitions(-DLIBSPDM_AEAD_SM4_128_GCM_SUPPORT=0) + + # Hard-disable ML-DSA and SLH-DSA for this mbedtls build. add_definitions(-DLIBSPDM_ML_DSA_44_SUPPORT=0) add_definitions(-DLIBSPDM_ML_DSA_65_SUPPORT=0) add_definitions(-DLIBSPDM_ML_DSA_87_SUPPORT=0) @@ -215,9 +214,32 @@ if(CRYPTO STREQUAL "mbedtls") add_definitions(-DLIBSPDM_SLH_DSA_SHAKE_256S_SUPPORT=0) add_definitions(-DLIBSPDM_SLH_DSA_SHA2_256F_SUPPORT=0) add_definitions(-DLIBSPDM_SLH_DSA_SHAKE_256F_SUPPORT=0) - add_definitions(-DLIBSPDM_ML_KEM_512_SUPPORT=0) - add_definitions(-DLIBSPDM_ML_KEM_768_SUPPORT=0) - add_definitions(-DLIBSPDM_ML_KEM_1024_SUPPORT=0) + + set(LIBSPDM_IPPCP_INCLUDE_DIR ${LIBSPDM_IPPCP_INCLUDE_DIR} CACHE PATH "Optional path to IPPCP include directory (containing ippcp.h)") + set(LIBSPDM_IPPCP_LIBRARY ${LIBSPDM_IPPCP_LIBRARY} CACHE FILEPATH "Optional path to IPPCP library (libippcp)") + + if(NOT LIBSPDM_IPPCP_INCLUDE_DIR) + find_path(LIBSPDM_IPPCP_INCLUDE_DIR NAMES ippcp.h) + endif() + + if(NOT LIBSPDM_IPPCP_LIBRARY) + find_library(LIBSPDM_IPPCP_LIBRARY NAMES ippcp) + endif() + + if(NOT LIBSPDM_IPPCP_INCLUDE_DIR OR NOT LIBSPDM_IPPCP_LIBRARY) + message(FATAL_ERROR "ML-KEM for CRYPTO=mbedtls requires ipp-crypto (IPPCP). Set LIBSPDM_IPPCP_INCLUDE_DIR and LIBSPDM_IPPCP_LIBRARY or install ipp-crypto.") + endif() + + message(STATUS "Enabling ML-KEM for mbedtls via IPPCP") + message(STATUS "IPPCP include: ${LIBSPDM_IPPCP_INCLUDE_DIR}") + message(STATUS "IPPCP library: ${LIBSPDM_IPPCP_LIBRARY}") + add_definitions(-DLIBSPDM_ML_KEM_512_SUPPORT=1) + add_definitions(-DLIBSPDM_ML_KEM_768_SUPPORT=1) + add_definitions(-DLIBSPDM_ML_KEM_1024_SUPPORT=1) + + add_definitions(-DLIBSPDM_SM2_DSA_P256_SUPPORT=0) + add_definitions(-DLIBSPDM_SM2_KEY_EXCHANGE_P256_SUPPORT=0) + add_definitions(-DLIBSPDM_AEAD_SM4_128_GCM_SUPPORT=0) elseif(CRYPTO STREQUAL "openssl") message("CRYPTO = openssl") add_definitions(-DLIBSPDM_SM2_KEY_EXCHANGE_P256_SUPPORT=0) @@ -1114,6 +1136,7 @@ else() add_subdirectory(unit_test/fuzzing/test_secured_message/test_spdm_decode_secured_message) add_subdirectory(unit_test/fuzzing/test_secured_message/test_spdm_encode_secured_message) add_subdirectory(unit_test/fuzzing/test_spdm_crypt/test_x509_certificate_check) + add_subdirectory(unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api) add_subdirectory(unit_test/fuzzing/test_responder/test_spdm_responder_set_certificate) add_subdirectory(unit_test/fuzzing/test_requester/test_spdm_requester_set_certificate) add_subdirectory(unit_test/fuzzing/test_responder/test_spdm_responder_csr) diff --git a/os_stub/cryptlib_mbedtls/CMakeLists.txt b/os_stub/cryptlib_mbedtls/CMakeLists.txt index 0fe9c8ee8fc..10331b8a625 100644 --- a/os_stub/cryptlib_mbedtls/CMakeLists.txt +++ b/os_stub/cryptlib_mbedtls/CMakeLists.txt @@ -15,6 +15,8 @@ target_include_directories(cryptlib_mbedtls ${LIBSPDM_DIR}/os_stub/mbedtlslib/mbedtls/include/mbedtls ) +target_include_directories(cryptlib_mbedtls PRIVATE ${LIBSPDM_IPPCP_INCLUDE_DIR}) + target_sources(cryptlib_mbedtls PRIVATE cipher/aead_aes_gcm.c @@ -65,3 +67,6 @@ if(MBEDTLS_CONFIG_FILE) else() target_compile_definitions(cryptlib_mbedtls PRIVATE -DMBEDTLS_CONFIG_FILE=) endif() + +# cryptlib_mbedtls is a static library; keep IPPCP dependency visible to final linkers. +target_link_libraries(cryptlib_mbedtls PUBLIC ${LIBSPDM_IPPCP_LIBRARY}) diff --git a/os_stub/cryptlib_mbedtls/pk/mlkem.c b/os_stub/cryptlib_mbedtls/pk/mlkem.c index 91a9ea3b561..d2a6eaef7d0 100644 --- a/os_stub/cryptlib_mbedtls/pk/mlkem.c +++ b/os_stub/cryptlib_mbedtls/pk/mlkem.c @@ -5,9 +5,127 @@ **/ #include "internal_crypt_lib.h" +#include "library/spdm_crypt_lib.h" #if LIBSPDM_ML_KEM_SUPPORT +#ifndef IPPCP_PREVIEW_ML_KEM +#define IPPCP_PREVIEW_ML_KEM +#endif +#include + +typedef struct { + IppsMLKEMParamSet param_set; + IppsMLKEMInfo info; + IppsMLKEMState *state; + uint8_t *decap_key; + bool has_decap_key; +} libspdm_mlkem_context; + +static bool libspdm_mlkem_nid_to_param_set(size_t nid, IppsMLKEMParamSet *param_set) +{ + if (param_set == NULL) { + return false; + } + + switch (nid) { + case LIBSPDM_CRYPTO_NID_ML_KEM_512: + *param_set = IPPCP_ML_KEM_512; + return true; + case LIBSPDM_CRYPTO_NID_ML_KEM_768: + *param_set = IPPCP_ML_KEM_768; + return true; + case LIBSPDM_CRYPTO_NID_ML_KEM_1024: + *param_set = IPPCP_ML_KEM_1024; + return true; + default: + return false; + } +} + +static IppStatus libspdm_ippcp_bit_supplier(Ipp32u *p_rand, int n_bits, void *p_ebs_params) +{ + size_t rand_size; + uint8_t *rand_bytes; + + (void)p_ebs_params; + + if ((p_rand == NULL) || (n_bits <= 0)) { + return ippStsErr; + } + + rand_bytes = (uint8_t *)p_rand; + rand_size = (size_t)(n_bits + 7) / 8; + if (!libspdm_get_random_number(rand_size, rand_bytes)) { + return ippStsErr; + } + + if ((n_bits & 0x7) != 0) { + rand_bytes[rand_size - 1] &= (uint8_t)((1u << (n_bits & 0x7)) - 1u); + } + + return ippStsNoErr; +} + +static void libspdm_mlkem_free_internal(libspdm_mlkem_context *ctx) +{ + if (ctx == NULL) { + return; + } + + if (ctx->decap_key != NULL) { + libspdm_zero_mem(ctx->decap_key, (size_t)ctx->info.decapsKeySize); + free_pool(ctx->decap_key); + } + if (ctx->state != NULL) { + free_pool(ctx->state); + } + libspdm_zero_mem(ctx, sizeof(*ctx)); + free_pool(ctx); +} + +static uint8_t *libspdm_mlkem_alloc_scratch_keygen(libspdm_mlkem_context *ctx) +{ + int scratch_size; + + if ((ctx == NULL) || (ctx->state == NULL)) { + return NULL; + } + if (ippsMLKEM_KeyGenBufferGetSize(&scratch_size, ctx->state) != ippStsNoErr) { + return NULL; + } + + return (uint8_t *)allocate_pool((size_t)scratch_size); +} + +static uint8_t *libspdm_mlkem_alloc_scratch_encaps(libspdm_mlkem_context *ctx) +{ + int scratch_size; + + if ((ctx == NULL) || (ctx->state == NULL)) { + return NULL; + } + if (ippsMLKEM_EncapsBufferGetSize(&scratch_size, ctx->state) != ippStsNoErr) { + return NULL; + } + + return (uint8_t *)allocate_pool((size_t)scratch_size); +} + +static uint8_t *libspdm_mlkem_alloc_scratch_decaps(libspdm_mlkem_context *ctx) +{ + int scratch_size; + + if ((ctx == NULL) || (ctx->state == NULL)) { + return NULL; + } + if (ippsMLKEM_DecapsBufferGetSize(&scratch_size, ctx->state) != ippStsNoErr) { + return NULL; + } + + return (uint8_t *)allocate_pool((size_t)scratch_size); +} + /** * Allocates and initializes one KEM context for subsequent use with the NID. * @@ -17,6 +135,51 @@ **/ void *libspdm_mlkem_new_by_name(size_t nid) { + libspdm_mlkem_context *ctx; + int state_size; + + ctx = allocate_zero_pool(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + if (!libspdm_mlkem_nid_to_param_set(nid, &ctx->param_set)) { + goto error; + } + + if (ippsMLKEM_GetInfo(&ctx->info, ctx->param_set) != ippStsNoErr) { + goto error; + } + if ((ctx->info.encapsKeySize <= 0) || (ctx->info.decapsKeySize <= 0) || + (ctx->info.cipherTextSize <= 0) || (ctx->info.sharedSecretSize <= 0)) { + goto error; + } + + if (ippsMLKEM_GetSize(&state_size, ctx->param_set) != ippStsNoErr) { + goto error; + } + if (state_size <= 0) { + goto error; + } + + ctx->state = (IppsMLKEMState *)allocate_zero_pool((size_t)state_size); + if (ctx->state == NULL) { + goto error; + } + if (ippsMLKEM_Init(ctx->state, ctx->param_set) != ippStsNoErr) { + goto error; + } + + ctx->decap_key = (uint8_t *)allocate_zero_pool((size_t)ctx->info.decapsKeySize); + if (ctx->decap_key == NULL) { + goto error; + } + + ctx->has_decap_key = false; + return ctx; + +error: + libspdm_mlkem_free_internal(ctx); return NULL; } @@ -27,6 +190,7 @@ void *libspdm_mlkem_new_by_name(size_t nid) **/ void libspdm_mlkem_free(void *kem_context) { + libspdm_mlkem_free_internal((libspdm_mlkem_context *)kem_context); } /** @@ -45,7 +209,37 @@ void libspdm_mlkem_free(void *kem_context) **/ bool libspdm_mlkem_generate_key(void *kem_context, uint8_t *encap_key, size_t *encap_key_size) { - return false; + libspdm_mlkem_context *ctx; + uint8_t *scratch; + size_t required_size; + IppStatus status; + + if ((kem_context == NULL) || (encap_key == NULL) || (encap_key_size == NULL)) { + return false; + } + + ctx = (libspdm_mlkem_context *)kem_context; + required_size = (size_t)ctx->info.encapsKeySize; + if (*encap_key_size < required_size) { + *encap_key_size = required_size; + return false; + } + + scratch = libspdm_mlkem_alloc_scratch_keygen(ctx); + if (scratch == NULL) { + return false; + } + + status = ippsMLKEM_KeyGen(encap_key, ctx->decap_key, ctx->state, + scratch, libspdm_ippcp_bit_supplier, NULL); + free_pool(scratch); + if (status != ippStsNoErr) { + return false; + } + + ctx->has_decap_key = true; + *encap_key_size = required_size; + return true; } /** @@ -69,7 +263,50 @@ bool libspdm_mlkem_encapsulate(void *kem_context, const uint8_t *peer_encap_key, size_t *cipher_text_size, uint8_t *shared_secret, size_t *shared_secret_size) { - return false; + libspdm_mlkem_context *ctx; + uint8_t *scratch; + size_t required_cipher_text_size; + size_t required_shared_secret_size; + IppStatus status; + + if ((kem_context == NULL) || (peer_encap_key == NULL) || (cipher_text == NULL) || + (cipher_text_size == NULL) || (shared_secret == NULL) || + (shared_secret_size == NULL)) { + return false; + } + + ctx = (libspdm_mlkem_context *)kem_context; + if (peer_encap_key_size != (size_t)ctx->info.encapsKeySize) { + return false; + } + + required_cipher_text_size = (size_t)ctx->info.cipherTextSize; + required_shared_secret_size = (size_t)ctx->info.sharedSecretSize; + if (*cipher_text_size < required_cipher_text_size) { + *cipher_text_size = required_cipher_text_size; + return false; + } + if (*shared_secret_size < required_shared_secret_size) { + *shared_secret_size = required_shared_secret_size; + return false; + } + + scratch = libspdm_mlkem_alloc_scratch_encaps(ctx); + if (scratch == NULL) { + return false; + } + + status = ippsMLKEM_Encaps(peer_encap_key, cipher_text, shared_secret, + ctx->state, scratch, + libspdm_ippcp_bit_supplier, NULL); + free_pool(scratch); + if (status != ippStsNoErr) { + return false; + } + + *cipher_text_size = required_cipher_text_size; + *shared_secret_size = required_shared_secret_size; + return true; } /** @@ -92,7 +329,44 @@ bool libspdm_mlkem_decapsulate(void *kem_context, const uint8_t *peer_cipher_tex size_t peer_cipher_text_size, uint8_t *shared_secret, size_t *shared_secret_size) { - return false; + libspdm_mlkem_context *ctx; + uint8_t *scratch; + size_t required_shared_secret_size; + IppStatus status; + + if ((kem_context == NULL) || (peer_cipher_text == NULL) || + (shared_secret == NULL) || (shared_secret_size == NULL)) { + return false; + } + + ctx = (libspdm_mlkem_context *)kem_context; + if (!ctx->has_decap_key) { + return false; + } + if (peer_cipher_text_size != (size_t)ctx->info.cipherTextSize) { + return false; + } + + required_shared_secret_size = (size_t)ctx->info.sharedSecretSize; + if (*shared_secret_size < required_shared_secret_size) { + *shared_secret_size = required_shared_secret_size; + return false; + } + + scratch = libspdm_mlkem_alloc_scratch_decaps(ctx); + if (scratch == NULL) { + return false; + } + + status = ippsMLKEM_Decaps(ctx->decap_key, peer_cipher_text, + shared_secret, ctx->state, scratch); + free_pool(scratch); + if (status != ippStsNoErr) { + return false; + } + + *shared_secret_size = required_shared_secret_size; + return true; } #ifdef LIBSPDM_FIPS_MODE @@ -124,7 +398,13 @@ bool libspdm_mlkem_encapsulate_ex(void *kem_context, const uint8_t *peer_encap_k size_t *shared_secret_size, uint8_t *entropy, size_t entropy_size) { - return false; + if ((entropy != NULL) || (entropy_size != 0)) { + return false; + } + return libspdm_mlkem_encapsulate(kem_context, peer_encap_key, + peer_encap_key_size, + cipher_text, cipher_text_size, + shared_secret, shared_secret_size); } /** @@ -138,7 +418,21 @@ bool libspdm_mlkem_encapsulate_ex(void *kem_context, const uint8_t *peer_encap_k **/ bool libspdm_mlkem_set_privkey(void *kem_context, const uint8_t *key_data, size_t key_size) { - return false; + libspdm_mlkem_context *ctx; + + if ((kem_context == NULL) || (key_data == NULL)) { + return false; + } + + ctx = (libspdm_mlkem_context *)kem_context; + if (key_size != (size_t)ctx->info.decapsKeySize) { + return false; + } + + libspdm_copy_mem(ctx->decap_key, (size_t)ctx->info.decapsKeySize, + key_data, key_size); + ctx->has_decap_key = true; + return true; } #endif /* LIBSPDM_FIPS_MODE */ #endif /* LIBSPDM_ML_KEM_SUPPORT */ diff --git a/unit_test/fuzzing/fuzzing_AFL.sh b/unit_test/fuzzing/fuzzing_AFL.sh index 8691290133a..2ee40f1e779 100755 --- a/unit_test/fuzzing/fuzzing_AFL.sh +++ b/unit_test/fuzzing/fuzzing_AFL.sh @@ -151,6 +151,7 @@ test_spdm_responder_key_update test_spdm_responder_end_session test_spdm_responder_if_ready test_x509_certificate_check +test_mlkem_kem_api test_spdm_responder_set_certificate test_spdm_requester_set_certificate test_spdm_responder_csr diff --git a/unit_test/fuzzing/fuzzing_AFLTurbo.sh b/unit_test/fuzzing/fuzzing_AFLTurbo.sh index f150a543020..604bece8874 100755 --- a/unit_test/fuzzing/fuzzing_AFLTurbo.sh +++ b/unit_test/fuzzing/fuzzing_AFLTurbo.sh @@ -152,6 +152,7 @@ test_spdm_responder_key_update test_spdm_responder_end_session test_spdm_responder_if_ready test_x509_certificate_check +test_mlkem_kem_api test_spdm_responder_set_certificate test_spdm_requester_set_certificate test_spdm_responder_csr diff --git a/unit_test/fuzzing/fuzzing_AFLplusplus.sh b/unit_test/fuzzing/fuzzing_AFLplusplus.sh index 25a944426c3..13a9c7fbebb 100755 --- a/unit_test/fuzzing/fuzzing_AFLplusplus.sh +++ b/unit_test/fuzzing/fuzzing_AFLplusplus.sh @@ -162,6 +162,7 @@ test_spdm_responder_key_update test_spdm_responder_end_session test_spdm_responder_if_ready test_x509_certificate_check +test_mlkem_kem_api test_spdm_responder_set_certificate test_spdm_requester_set_certificate test_spdm_responder_csr diff --git a/unit_test/fuzzing/fuzzing_LibFuzzer.sh b/unit_test/fuzzing/fuzzing_LibFuzzer.sh index 97960437f69..b03e3db1f34 100755 --- a/unit_test/fuzzing/fuzzing_LibFuzzer.sh +++ b/unit_test/fuzzing/fuzzing_LibFuzzer.sh @@ -122,6 +122,7 @@ test_spdm_responder_key_update test_spdm_responder_end_session test_spdm_responder_if_ready test_x509_certificate_check +test_mlkem_kem_api test_spdm_responder_set_certificate test_spdm_requester_set_certificate test_spdm_responder_csr diff --git a/unit_test/fuzzing/run_initial_seed.sh b/unit_test/fuzzing/run_initial_seed.sh index cf4d2b0b907..674390dd787 100755 --- a/unit_test/fuzzing/run_initial_seed.sh +++ b/unit_test/fuzzing/run_initial_seed.sh @@ -53,6 +53,7 @@ test_spdm_responder_key_update test_spdm_responder_end_session test_spdm_responder_if_ready test_x509_certificate_check +test_mlkem_kem_api test_spdm_requester_vendor_cmds test_spdm_responder_vendor_cmds test_spdm_responder_measurement_extension_log diff --git a/unit_test/fuzzing/seeds/test_mlkem_kem_api/seed-01.raw b/unit_test/fuzzing/seeds/test_mlkem_kem_api/seed-01.raw new file mode 100644 index 00000000000..c2eb499c8ad Binary files /dev/null and b/unit_test/fuzzing/seeds/test_mlkem_kem_api/seed-01.raw differ diff --git a/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/CMakeLists.txt b/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/CMakeLists.txt new file mode 100644 index 00000000000..866a062683c --- /dev/null +++ b/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.5) + +add_executable(test_mlkem_kem_api) + +target_include_directories(test_mlkem_kem_api + PRIVATE + ${LIBSPDM_DIR}/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api + ${LIBSPDM_DIR}/include + ${LIBSPDM_DIR}/unit_test/include + ${LIBSPDM_DIR}/unit_test/fuzzing/spdm_unit_fuzzing_common + ${LIBSPDM_DIR}/os_stub/include +) + +if(TOOLCHAIN STREQUAL "KLEE") + target_include_directories(test_mlkem_kem_api + PRIVATE + $ENV{KLEE_SRC_PATH}/include + ) +endif() + +target_sources(test_mlkem_kem_api + PRIVATE + mlkem_kem_api.c + ${PROJECT_SOURCE_DIR}/unit_test/fuzzing/spdm_unit_fuzzing_common/common.c + ${PROJECT_SOURCE_DIR}/unit_test/fuzzing/spdm_unit_fuzzing_common/toolchain_harness.c +) + +if((TOOLCHAIN STREQUAL "KLEE") OR (TOOLCHAIN STREQUAL "CBMC")) + target_link_libraries(test_mlkem_kem_api + PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +else() + target_link_libraries(test_mlkem_kem_api + PRIVATE + memlib + debuglib + spdm_responder_lib + spdm_common_lib + ${CRYPTO_LIB_PATHS} + rnglib + cryptlib_${CRYPTO} + malloclib + spdm_crypt_lib + spdm_secured_message_lib + spdm_transport_test_lib + spdm_device_secret_lib_null + platform_lib_null + ) +endif() diff --git a/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/mlkem_kem_api.c b/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/mlkem_kem_api.c new file mode 100644 index 00000000000..2cf0e18d1df --- /dev/null +++ b/unit_test/fuzzing/test_spdm_crypt/test_mlkem_kem_api/mlkem_kem_api.c @@ -0,0 +1,159 @@ +/** + * Copyright Notice: + * Copyright 2026 DMTF. All rights reserved. + * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md + **/ + +#include "library/spdm_crypt_lib.h" +#include "spdm_unit_fuzzing.h" +#include "toolchain_harness.h" + +size_t libspdm_get_max_buffer_size(void) +{ + return 4096; +} + +static uint8_t libspdm_get_data_u8(const uint8_t *buffer, size_t buffer_size, + size_t *offset, uint8_t default_value) +{ + if (*offset >= buffer_size) { + return default_value; + } + return buffer[(*offset)++]; +} + +static size_t libspdm_get_data_block(const uint8_t *buffer, size_t buffer_size, + size_t *offset, uint8_t *output, + size_t output_size) +{ + size_t available; + size_t copy_size; + + if ((*offset >= buffer_size) || (output_size == 0)) { + return 0; + } + + available = buffer_size - *offset; + copy_size = available; + if (copy_size > output_size) { + copy_size = output_size; + } + + libspdm_copy_mem(output, output_size, buffer + *offset, copy_size); + *offset += copy_size; + return copy_size; +} + +void libspdm_run_test_harness(void *test_buffer, size_t test_buffer_size) +{ + const uint8_t *buffer; + size_t offset; + size_t nids[4] = { + LIBSPDM_CRYPTO_NID_ML_KEM_512, + LIBSPDM_CRYPTO_NID_ML_KEM_768, + LIBSPDM_CRYPTO_NID_ML_KEM_1024, + (size_t)-1 + }; + size_t local_nid; + void *kem_local; + void *kem_peer; + uint8_t peer_public_key[1568]; + uint8_t cipher_text[1568]; + uint8_t shared_secret[64]; + size_t peer_public_key_size; + size_t cipher_text_size; + size_t shared_secret_size; + bool status; + + if ((test_buffer == NULL) || (test_buffer_size == 0)) { + return; + } + + buffer = (const uint8_t *)test_buffer; + offset = 0; + + local_nid = nids[libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0) % 4]; + kem_local = libspdm_mlkem_new_by_name(local_nid); + if (kem_local == NULL) { + return; + } + + kem_peer = libspdm_mlkem_new_by_name( + nids[libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0) % 3]); + if (kem_peer == NULL) { + libspdm_mlkem_free(kem_local); + return; + } + + peer_public_key_size = + (size_t)libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0); + status = libspdm_mlkem_generate_key(kem_peer, peer_public_key, + &peer_public_key_size); + if (!status) { + peer_public_key_size = sizeof(peer_public_key); + status = libspdm_mlkem_generate_key(kem_peer, peer_public_key, + &peer_public_key_size); + } + + if ((libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0) & 0x1) != 0) { + /* Exercise invalid public-key size path. */ + cipher_text_size = sizeof(cipher_text); + shared_secret_size = sizeof(shared_secret); + (void)libspdm_mlkem_encapsulate(kem_local, peer_public_key, + peer_public_key_size == 0 ? 1 : peer_public_key_size - 1, + cipher_text, &cipher_text_size, + shared_secret, &shared_secret_size); + } + + if (status) { + cipher_text_size = + (size_t)libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0); + shared_secret_size = + (size_t)libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0); + + if (cipher_text_size == 0) { + cipher_text_size = sizeof(cipher_text); + } + if (shared_secret_size == 0) { + shared_secret_size = sizeof(shared_secret); + } + + status = libspdm_mlkem_encapsulate(kem_local, peer_public_key, + peer_public_key_size, + cipher_text, &cipher_text_size, + shared_secret, &shared_secret_size); + + if (status && + ((libspdm_get_data_u8(buffer, test_buffer_size, &offset, 0) & 0x1) != 0)) { + size_t bad_shared_secret_size = + (size_t)libspdm_get_data_u8(buffer, test_buffer_size, &offset, 1); + (void)libspdm_mlkem_decapsulate(kem_peer, cipher_text, + cipher_text_size, + shared_secret, + &bad_shared_secret_size); + } + + if (status) { + shared_secret_size = sizeof(shared_secret); + (void)libspdm_mlkem_decapsulate(kem_peer, cipher_text, + cipher_text_size, + shared_secret, + &shared_secret_size); + } + } + + /* Exercise decapsulation with fuzzer-provided ciphertext too. */ + cipher_text_size = + libspdm_get_data_block(buffer, test_buffer_size, &offset, + cipher_text, sizeof(cipher_text)); + if (cipher_text_size > 0) { + shared_secret_size = sizeof(shared_secret); + (void)libspdm_mlkem_decapsulate(kem_peer, cipher_text, + cipher_text_size, + shared_secret, + &shared_secret_size); + } + + libspdm_mlkem_free(kem_peer); + libspdm_mlkem_free(kem_local); +} diff --git a/unit_test/test_crypt/CMakeLists.txt b/unit_test/test_crypt/CMakeLists.txt index b28390751f4..6e34faa6f48 100644 --- a/unit_test/test_crypt/CMakeLists.txt +++ b/unit_test/test_crypt/CMakeLists.txt @@ -31,6 +31,7 @@ target_sources(test_crypt rand_verify.c x509_verify.c mlkem_verify.c + mlkem_negative_verify.c mldsa_verify.c mldsa_verify2.c slhdsa_verify.c diff --git a/unit_test/test_crypt/mlkem_negative_verify.c b/unit_test/test_crypt/mlkem_negative_verify.c new file mode 100644 index 00000000000..e2ad04f9053 --- /dev/null +++ b/unit_test/test_crypt/mlkem_negative_verify.c @@ -0,0 +1,195 @@ +/** + * Copyright Notice: + * Copyright 2026 DMTF. All rights reserved. + * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md + **/ + +#include "test_crypt.h" + +#if LIBSPDM_ML_KEM_SUPPORT + +/** + * Validate Crypto ML-KEM input validation paths. + * + * @retval true Validation succeeded. + * @retval false Validation failed. + * + **/ +bool libspdm_validate_crypt_mlkem_negative(void) +{ + void *mlkem1; + void *mlkem2; + bool status; + uint8_t encap_key1[1568]; + size_t encap_key1_length; + uint8_t cipher_text2[1568]; + size_t cipher_text2_length; + uint8_t shared_secret1[32]; + size_t shared_secret1_length; + uint8_t shared_secret2[32]; + size_t shared_secret2_length; + + libspdm_my_print("\nCrypto ML-KEM Negative Testing:\n"); + + libspdm_my_print("- Invalid NID ... "); + mlkem1 = libspdm_mlkem_new_by_name((size_t)-1); + if (mlkem1 != NULL) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + return false; + } + + libspdm_my_print("Context1 ... "); + mlkem1 = libspdm_mlkem_new_by_name(LIBSPDM_CRYPTO_NID_ML_KEM_512); + if (mlkem1 == NULL) { + libspdm_my_print("[Fail]"); + return false; + } + + libspdm_my_print("Context2 ... "); + mlkem2 = libspdm_mlkem_new_by_name(LIBSPDM_CRYPTO_NID_ML_KEM_512); + if (mlkem2 == NULL) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + return false; + } + + libspdm_my_print("Generate key with too small output ... "); + encap_key1_length = 1; + status = libspdm_mlkem_generate_key(mlkem1, encap_key1, &encap_key1_length); + if (status || encap_key1_length != 800) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Decapsulate before key generation ... "); + shared_secret1_length = sizeof(shared_secret1); + status = libspdm_mlkem_decapsulate(mlkem2, cipher_text2, 768, + shared_secret1, &shared_secret1_length); + if (status) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Generate valid encap key ... "); + encap_key1_length = sizeof(encap_key1); + status = libspdm_mlkem_generate_key(mlkem1, encap_key1, &encap_key1_length); + if (!status || encap_key1_length != 800) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Encapsulate with invalid public-key size ... "); + cipher_text2_length = sizeof(cipher_text2); + shared_secret2_length = sizeof(shared_secret2); + status = libspdm_mlkem_encapsulate(mlkem2, encap_key1, encap_key1_length - 1, + cipher_text2, &cipher_text2_length, + shared_secret2, &shared_secret2_length); + if (status) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Encapsulate with too small ciphertext buffer ... "); + cipher_text2_length = 1; + shared_secret2_length = sizeof(shared_secret2); + status = libspdm_mlkem_encapsulate(mlkem2, encap_key1, encap_key1_length, + cipher_text2, &cipher_text2_length, + shared_secret2, &shared_secret2_length); + if (status || cipher_text2_length != 768) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Encapsulate with too small shared-secret buffer ... "); + cipher_text2_length = sizeof(cipher_text2); + shared_secret2_length = 1; + status = libspdm_mlkem_encapsulate(mlkem2, encap_key1, encap_key1_length, + cipher_text2, &cipher_text2_length, + shared_secret2, &shared_secret2_length); + if (status || shared_secret2_length != 32) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Encapsulate valid path ... "); + cipher_text2_length = sizeof(cipher_text2); + shared_secret2_length = sizeof(shared_secret2); + status = libspdm_mlkem_encapsulate(mlkem2, encap_key1, encap_key1_length, + cipher_text2, &cipher_text2_length, + shared_secret2, &shared_secret2_length); + if (!status || cipher_text2_length != 768 || shared_secret2_length != 32) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Decapsulate with invalid ciphertext size ... "); + shared_secret1_length = sizeof(shared_secret1); + status = libspdm_mlkem_decapsulate(mlkem1, cipher_text2, cipher_text2_length - 1, + shared_secret1, &shared_secret1_length); + if (status) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Decapsulate with too small output buffer ... "); + shared_secret1_length = 1; + status = libspdm_mlkem_decapsulate(mlkem1, cipher_text2, cipher_text2_length, + shared_secret1, &shared_secret1_length); + if (status || shared_secret1_length != 32) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Decapsulate valid path ... "); + shared_secret1_length = sizeof(shared_secret1); + status = libspdm_mlkem_decapsulate(mlkem1, cipher_text2, cipher_text2_length, + shared_secret1, &shared_secret1_length); + if (!status || shared_secret1_length != 32) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("Compare Keys ... "); + if (shared_secret1_length != shared_secret2_length) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + if (memcmp(shared_secret1, shared_secret2, shared_secret1_length) != 0) { + libspdm_my_print("[Fail]"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + return false; + } + + libspdm_my_print("[Pass]\n"); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); + + return true; +} + +#endif /* LIBSPDM_ML_KEM_SUPPORT */ diff --git a/unit_test/test_crypt/mlkem_verify.c b/unit_test/test_crypt/mlkem_verify.c index 675d8df1049..b5082872f17 100644 --- a/unit_test/test_crypt/mlkem_verify.c +++ b/unit_test/test_crypt/mlkem_verify.c @@ -46,7 +46,7 @@ bool libspdm_validate_crypt_mlkem(void) mlkem2 = libspdm_mlkem_new_by_name(LIBSPDM_CRYPTO_NID_ML_KEM_512); if (mlkem2 == NULL) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); + libspdm_mlkem_free(mlkem1); return false; } @@ -54,8 +54,8 @@ bool libspdm_validate_crypt_mlkem(void) status = libspdm_mlkem_generate_key(mlkem1, encap_key1, &encap_key1_length); if (!status || encap_key1_length != 800) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return false; } @@ -65,8 +65,8 @@ bool libspdm_validate_crypt_mlkem(void) shared_secret2, &shared_secret2_length); if (!status || cipher_text2_length != 768 || shared_secret2_length != 32) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return false; } @@ -75,29 +75,29 @@ bool libspdm_validate_crypt_mlkem(void) shared_secret1, &shared_secret1_length); if (!status || shared_secret1_length != 32) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return false; } libspdm_my_print("Compare Keys ... "); if (shared_secret1_length != shared_secret2_length) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return false; } if (memcmp(shared_secret1, shared_secret2, shared_secret1_length) != 0) { libspdm_my_print("[Fail]"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return false; } libspdm_my_print("[Pass]\n"); - libspdm_dh_free(mlkem1); - libspdm_dh_free(mlkem2); + libspdm_mlkem_free(mlkem1); + libspdm_mlkem_free(mlkem2); return true; } diff --git a/unit_test/test_crypt/test_crypt.c b/unit_test/test_crypt/test_crypt.c index 80b97b00c22..a4b4e5e07fc 100644 --- a/unit_test/test_crypt/test_crypt.c +++ b/unit_test/test_crypt/test_crypt.c @@ -189,6 +189,11 @@ bool libspdm_cryptest_main(void) if (!status) { return status; } + + status = libspdm_validate_crypt_mlkem_negative(); + if (!status) { + return status; + } #endif /* LIBSPDM_ML_KEM_SUPPORT */ #if LIBSPDM_ML_DSA_SUPPORT diff --git a/unit_test/test_crypt/test_crypt.h b/unit_test/test_crypt/test_crypt.h index d0b6f91226b..ed22bc158b2 100644 --- a/unit_test/test_crypt/test_crypt.h +++ b/unit_test/test_crypt/test_crypt.h @@ -175,6 +175,15 @@ bool libspdm_validate_crypt_prng(void); **/ bool libspdm_validate_crypt_mlkem(void); +/** + * Validate Crypto MLKEM input validation paths. + * + * @retval true Validation succeeded. + * @retval false Validation failed. + * + **/ +bool libspdm_validate_crypt_mlkem_negative(void); + /** * Validate Crypto MLDSA Interfaces. *