Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/wp_mlkem_kem.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ static int wp_mlkem_kem_encapsulate(wp_MlKemCtx* ctx, unsigned char* out,
ctSize = wp_mlkem_data_ct_size(data);
ssSize = WP_MLKEM_SS_SIZE;

/* Size-only query: both output buffers NULL. A mixed-NULL request (one
* buffer NULL, the other not) is a caller bug, not a size query. */
if ((out == NULL) && (secret == NULL)) {
/* OpenSSL permits a size query with no ciphertext output buffer while
* the caller supplies the shared-secret buffer. */
if (out == NULL) {
if (outLen != NULL) {
*outLen = ctSize;
}
Expand All @@ -223,8 +223,7 @@ static int wp_mlkem_kem_encapsulate(wp_MlKemCtx* ctx, unsigned char* out,
WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 1);
return 1;
}
if ((out == NULL) || (secret == NULL) || (outLen == NULL) ||
(secretLen == NULL)) {
if ((secret == NULL) || (outLen == NULL) || (secretLen == NULL)) {
WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 0);
return 0;
}
Expand Down
5 changes: 2 additions & 3 deletions src/wp_mlx_kem.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ static int wp_mlx_kem_encapsulate(wp_MlxCtx* ctx, unsigned char* out,
ctSize = (size_t)data->mlkemCtSize + data->classicalPubSize;
ssSize = (size_t)WP_MLKEM_SS_SIZE + data->classicalShSecSize;

if ((out == NULL) && (secret == NULL)) {
if (out == NULL) {
if (outLen != NULL) {
*outLen = ctSize;
}
Expand All @@ -288,8 +288,7 @@ static int wp_mlx_kem_encapsulate(wp_MlxCtx* ctx, unsigned char* out,
}
return 1;
}
if ((out == NULL) || (secret == NULL) || (outLen == NULL) ||
(secretLen == NULL)) {
if ((secret == NULL) || (outLen == NULL) || (secretLen == NULL)) {
return 0;
}
if ((*outLen < ctSize) || (*secretLen < ssSize)) {
Expand Down
113 changes: 111 additions & 2 deletions test/test_mlkem.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ static const mlkem_test_level mlkem_levels[] = {
* @param [out] pkey Generated EVP_PKEY (caller frees).
* @return 0 on success, non-zero on failure.
*/
static int wp_test_mlkem_keygen(const char* name, EVP_PKEY** pkey)
static int wp_test_mlkem_keygen_ex(OSSL_LIB_CTX* libCtx, const char* prop,
const char* name, EVP_PKEY** pkey)
{
int err = 0;
EVP_PKEY_CTX* ctx = NULL;

ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, name, NULL);
ctx = EVP_PKEY_CTX_new_from_name(libCtx, name, prop);
err = (ctx == NULL);
if (err == 0) {
err = EVP_PKEY_keygen_init(ctx) != 1;
Expand All @@ -70,6 +71,11 @@ static int wp_test_mlkem_keygen(const char* name, EVP_PKEY** pkey)
return err;
}

static int wp_test_mlkem_keygen(const char* name, EVP_PKEY** pkey)
{
return wp_test_mlkem_keygen_ex(wpLibCtx, NULL, name, pkey);
}

/**
* Extract the raw public key bytes from an ML-KEM EVP_PKEY.
*
Expand Down Expand Up @@ -333,6 +339,109 @@ int test_mlkem_encap_decap(void* data)
return err;
}

typedef struct mlkem_null_result {
int rc;
size_t ctLen;
size_t secretLen;
} mlkem_null_result;

static int mlkem_null_results(OSSL_LIB_CTX* libCtx, const char* prop,
const mlkem_test_level* level, mlkem_null_result results[3])
{
int err = 0;
EVP_PKEY* pkey = NULL;
EVP_PKEY_CTX* ectx = NULL;
EVP_PKEY_CTX* dctx = NULL;
unsigned char ct[WC_ML_KEM_1024_CIPHER_TEXT_SIZE] = { 0 };
unsigned char secret[32] = { 0 };
size_t ctLen = sizeof(ct);
size_t secretLen = sizeof(secret);

err = wp_test_mlkem_keygen_ex(libCtx, prop, level->name, &pkey);
if (err == 0) {
ectx = EVP_PKEY_CTX_new_from_pkey(libCtx, pkey, prop);
err = (ectx == NULL) ||
(EVP_PKEY_encapsulate_init(ectx, NULL) != 1);
}
if (err == 0) {
results[0].rc = EVP_PKEY_encapsulate(ectx, NULL, &ctLen, secret,
&secretLen);
results[0].ctLen = ctLen;
results[0].secretLen = secretLen;
ctLen = sizeof(ct);
secretLen = sizeof(secret);
results[1].rc = EVP_PKEY_encapsulate(ectx, ct, &ctLen, NULL,
&secretLen);
results[1].ctLen = ctLen;
results[1].secretLen = secretLen;
}
if (err == 0) {
dctx = EVP_PKEY_CTX_new_from_pkey(libCtx, pkey, prop);
err = (dctx == NULL) ||
(EVP_PKEY_decapsulate_init(dctx, NULL) != 1);
}
if (err == 0) {
ctLen = level->ctSize;
secretLen = sizeof(secret);
results[2].rc = EVP_PKEY_decapsulate(dctx, secret, &secretLen, NULL,
ctLen);
results[2].ctLen = ctLen;
results[2].secretLen = secretLen;
}

EVP_PKEY_CTX_free(ectx);
EVP_PKEY_CTX_free(dctx);
EVP_PKEY_free(pkey);
return err;
}

/* Compare wolfProvider NULL handling directly with the OpenSSL provider. */
int test_mlkem_mixed_null(void* data)
{
int err = 0;
size_t i;

(void)data;
for (i = 0; (err == 0) && (i < MLKEM_LEVEL_COUNT); i++) {
mlkem_null_result osslResults[3] = { { 0 } };
mlkem_null_result wpResults[3] = { { 0 } };
size_t j;

PRINT_MSG("Mixed-NULL A/B %s", mlkem_levels[i].name);
err = mlkem_null_results(osslLibCtx, "provider=default",
&mlkem_levels[i], osslResults);
if (err == 0) {
err = mlkem_null_results(wpLibCtx, NULL, &mlkem_levels[i],
wpResults);
}
if (err == 0 && ((osslResults[0].rc != 1) ||
(osslResults[1].rc != 0) || (osslResults[2].rc != 0))) {
PRINT_ERR_MSG("Unexpected OpenSSL NULL handling: %d, %d, %d",
osslResults[0].rc, osslResults[1].rc, osslResults[2].rc);
err = 1;
}
if (err == 0 && ((osslResults[0].ctLen != mlkem_levels[i].ctSize) ||
(osslResults[0].secretLen != 32))) {
PRINT_ERR_MSG("Unexpected OpenSSL size query: ct=%zu, secret=%zu",
osslResults[0].ctLen, osslResults[0].secretLen);
err = 1;
}
for (j = 0; (err == 0) && (j < 3); j++) {
if ((osslResults[j].rc != wpResults[j].rc) ||
(osslResults[j].ctLen != wpResults[j].ctLen) ||
(osslResults[j].secretLen != wpResults[j].secretLen)) {
PRINT_ERR_MSG("wolfProvider differs from OpenSSL call %zu: "
"rc=%d/%d, ct=%zu/%zu, secret=%zu/%zu", j,
wpResults[j].rc, osslResults[j].rc,
wpResults[j].ctLen, osslResults[j].ctLen,
wpResults[j].secretLen, osslResults[j].secretLen);
err = 1;
}
}
}
return err;
}

/**
* Test ML-KEM decapsulate of a tampered ciphertext: must still succeed and
* yield a different shared secret (implicit rejection).
Expand Down
1 change: 1 addition & 0 deletions test/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ TEST_CASE test_case[] = {
TEST_DECL(test_mlkem_keygen, NULL),
TEST_DECL(test_mlkem_import_export_roundtrip, NULL),
TEST_DECL(test_mlkem_encap_decap, NULL),
TEST_DECL(test_mlkem_mixed_null, NULL),
TEST_DECL(test_mlkem_decap_tampered_ct, NULL),
TEST_DECL(test_mlkem_decap_wrong_key, NULL),
TEST_DECL(test_mlkem_dup, NULL),
Expand Down
1 change: 1 addition & 0 deletions test/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ int test_des3_tls_cbc_dec(void *data);
int test_mlkem_keygen(void *data);
int test_mlkem_import_export_roundtrip(void *data);
int test_mlkem_encap_decap(void *data);
int test_mlkem_mixed_null(void *data);
int test_mlkem_decap_tampered_ct(void *data);
int test_mlkem_decap_wrong_key(void *data);
int test_mlkem_dup(void *data);
Expand Down
Loading