Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .CMake/alg_support.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ cmake_dependent_option(OQS_ENABLE_KEM_ml_kem_768 "" ON "OQS_ENABLE_KEM_ML_KEM" O
cmake_dependent_option(OQS_ENABLE_KEM_ml_kem_1024 "" ON "OQS_ENABLE_KEM_ML_KEM" OFF)

option(OQS_ENABLE_SIG_ML_DSA "Enable ml_dsa algorithm family" ON)
cmake_dependent_option(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING "Enable randomized signing for ML-DSA" ON "OQS_ENABLE_SIG_ML_DSA" OFF)
cmake_dependent_option(OQS_ENABLE_SIG_ml_dsa_44 "" ON "OQS_ENABLE_SIG_ML_DSA" OFF)
cmake_dependent_option(OQS_ENABLE_SIG_ml_dsa_65 "" ON "OQS_ENABLE_SIG_ML_DSA" OFF)
cmake_dependent_option(OQS_ENABLE_SIG_ml_dsa_87 "" ON "OQS_ENABLE_SIG_ML_DSA" OFF)
Expand Down
8 changes: 7 additions & 1 deletion CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ To enable `XMSS` stateful signature, set `OQS_ENABLE_SIG_STFL_XMSS` to `ON`, the

For a full list of such options and their default values, consult [.CMake/alg_support.cmake](https://github.com/open-quantum-safe/liboqs/blob/master/.CMake/alg_support.cmake).

**Default**: Unset.
### OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING

Can be set to `ON` or `OFF`. When `ON`, ML-DSA signature algorithms are built with randomized signing enabled, resulting in non-deterministic signatures. When `OFF`, ML-DSA signature algorithms use deterministic signing.

This option is only available if `OQS_ENABLE_SIG_ML_DSA` is `ON`.

**Default**: `ON`.

## OQS_ALGS_ENABLED

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ cmake_dependent_option(OQS_ENABLE_KEM_{{ family['name'] }}_{{ scheme['alias_sche
option(OQS_ENABLE_SIG_{{ family['name']|upper }} "Enable {{ family['name'] }} algorithm family" OFF)
{%- else %}
option(OQS_ENABLE_SIG_{{ family['name']|upper }} "Enable {{ family['name'] }} algorithm family" ON)
{%- endif %}
{%- if family['name'] == "ml_dsa" %}
cmake_dependent_option(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING "Enable randomized signing for ML-DSA" ON "OQS_ENABLE_SIG_ML_DSA" OFF)
{%- endif %}
{%- for scheme in family['schemes'] %}
cmake_dependent_option(OQS_ENABLE_SIG_{{ family['name'] }}_{{ scheme['scheme'] }} "" ON "OQS_ENABLE_SIG_{{ family['name']|upper }}" OFF)
Expand Down
24 changes: 20 additions & 4 deletions scripts/copy_from_upstream/patches/pqcrystals-ml_dsa.patch
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,18 @@ index 5163526..e9bff1e 100644
- architecture: x86_64
operating_systems:
diff --git a/avx2/config.h b/avx2/config.h
index a9facc0..3944cb4 100644
index a9facc0..da97994 100644
--- a/avx2/config.h
+++ b/avx2/config.h
@@ -11,17 +11,17 @@
@@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
-#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

@@ -11,17 +10,17 @@
#endif

#if DILITHIUM_MODE == 2
Expand Down Expand Up @@ -520,10 +528,18 @@ index 8f3c3c5..fa49963 100644

#endif
diff --git a/ref/config.h b/ref/config.h
index 98b8ccb..8008e11 100644
index 98b8ccb..7d52ebc 100644
--- a/ref/config.h
+++ b/ref/config.h
@@ -11,17 +11,17 @@
@@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
-#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

@@ -11,17 +10,17 @@
#endif

#if DILITHIUM_MODE == 2
Expand Down
5 changes: 5 additions & 0 deletions scripts/copy_from_upstream/src/sig/family/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ if(OQS_ENABLE_SIG_{{ family }}_{{ scheme['scheme_c'] }}_{{ impl['name'] }}{%- if
{%- if impl['compile_opts'] %}
target_compile_options({{ family }}_{{ scheme['scheme'] }}_{{ impl['name'] }} PUBLIC {{ impl['compile_opts'] }})
{%- endif %}
{%- if family == 'ml_dsa' %}
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options({{ family }}_{{ scheme['scheme'] }}_{{ impl['name'] }} PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
{%- endif %}
set(_{{ family|upper }}_OBJS ${_{{ family|upper }}_OBJS} $<TARGET_OBJECTS:{{ family }}_{{ scheme['scheme'] }}_{{ impl['name'] }}>)
endif()
{%- endfor -%}
Expand Down
18 changes: 18 additions & 0 deletions src/sig/ml_dsa/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ if(OQS_ENABLE_SIG_ml_dsa_44)
target_include_directories(ml_dsa_44_ref PRIVATE ${CMAKE_CURRENT_LIST_DIR}/pqcrystals-dilithium-standard_ml-dsa-44_ref)
target_include_directories(ml_dsa_44_ref PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_44_ref PUBLIC -DDILITHIUM_MODE=2)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_44_ref PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_44_ref>)
endif()

Expand All @@ -20,6 +23,9 @@ if(OQS_ENABLE_SIG_ml_dsa_44_avx2)
target_include_directories(ml_dsa_44_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_44_avx2 PRIVATE -mavx2 -mpopcnt)
target_compile_options(ml_dsa_44_avx2 PUBLIC -DDILITHIUM_MODE=2)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_44_avx2 PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_44_avx2>)
endif()

Expand All @@ -29,6 +35,9 @@ if(OQS_ENABLE_SIG_ml_dsa_65)
target_include_directories(ml_dsa_65_ref PRIVATE ${CMAKE_CURRENT_LIST_DIR}/pqcrystals-dilithium-standard_ml-dsa-65_ref)
target_include_directories(ml_dsa_65_ref PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_65_ref PUBLIC -DDILITHIUM_MODE=3)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_65_ref PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_65_ref>)
endif()

Expand All @@ -38,6 +47,9 @@ if(OQS_ENABLE_SIG_ml_dsa_65_avx2)
target_include_directories(ml_dsa_65_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_65_avx2 PRIVATE -mavx2 -mpopcnt)
target_compile_options(ml_dsa_65_avx2 PUBLIC -DDILITHIUM_MODE=3)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_65_avx2 PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_65_avx2>)
endif()

Expand All @@ -47,6 +59,9 @@ if(OQS_ENABLE_SIG_ml_dsa_87)
target_include_directories(ml_dsa_87_ref PRIVATE ${CMAKE_CURRENT_LIST_DIR}/pqcrystals-dilithium-standard_ml-dsa-87_ref)
target_include_directories(ml_dsa_87_ref PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_87_ref PUBLIC -DDILITHIUM_MODE=5)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_87_ref PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_87_ref>)
endif()

Expand All @@ -56,6 +71,9 @@ if(OQS_ENABLE_SIG_ml_dsa_87_avx2)
target_include_directories(ml_dsa_87_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims)
target_compile_options(ml_dsa_87_avx2 PRIVATE -mavx2 -mpopcnt)
target_compile_options(ml_dsa_87_avx2 PUBLIC -DDILITHIUM_MODE=5)
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_options(ml_dsa_87_avx2 PUBLIC -DDILITHIUM_RANDOMIZED_SIGNING)
endif()
set(_ML_DSA_OBJS ${_ML_DSA_OBJS} $<TARGET_OBJECTS:ml_dsa_87_avx2>)
endif()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define CONFIG_H

//#define DILITHIUM_MODE 2
#define DILITHIUM_RANDOMIZED_SIGNING
//#define USE_RDPMC
//#define DBENCH

Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ endif()

add_executable(test_sig test_sig.c test_helpers.c)
target_link_libraries(test_sig PRIVATE ${TEST_DEPS})
if(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
target_compile_definitions(test_sig PRIVATE OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND BUILD_SHARED_LIBS)
# workaround for Windows .dll
if(CMAKE_CROSSCOMPILING)
Expand Down
11 changes: 11 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,17 @@ def available_use_options_by_name():
def is_use_option_enabled_by_name(name):
return name in available_use_options_by_name()

def is_ml_dsa_randomized_signing_enabled():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach feels "brittle" (but I may be paranoid here): For example, would it be possible to build everything with "-DOQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING" (passed to the compiler during a re-build)? If so, this function will fail to return the right value. And/or would it be sensible to have cmake variable and C define have different names instead? And then there's still the possibility that header file and binary under test don't match up, e.g., because of some user-specific LD_LIBRARY_PATH setup...
I still feel it would be good for users to have a way (e.g., static variable) to query which way (randomized or deterministic) the library under test/use has been built; this test function could use the same facility to pick which way to test (and test the user-level "build-type" facility at the same time).

header = os.path.join(get_current_build_dir_name(), 'include', 'oqs', 'oqsconfig.h')
try:
with open(header) as fh:
for line in fh:
if line.strip() == "#define OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING":
return True
except Exception:
pass
return False

def get_kats(t):
if kats[t] is None:
with open(os.path.join('tests', 'KATs', t, 'kats.json'), 'r') as fp:
Expand Down
3 changes: 3 additions & 0 deletions tests/test_acvp_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def test_acvp_vec_ml_dsa_sig_gen(sig_name):
if variant["parameterSet"] == sig_name:
variantFound = True
for testCase in variant["tests"]:
# Skip randomized signature vectors if built in deterministic mode
if (not helpers.is_ml_dsa_randomized_signing_enabled()) and (not variant["deterministic"]):
pytest.skip("Skipping randomized signature vector test: implementation built in deterministic mode")
sk = testCase["sk"]
message = testCase["message"]
signature = testCase["signature"]
Expand Down
3 changes: 3 additions & 0 deletions tests/test_kat.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def test_sig(sig_name):
kats = helpers.get_kats("sig")
# slh dsa will run ACVP vectors instead
if ("SLH_DSA" in sig_name): pytest.skip('slhdsa not enabled for KATs')
# Skip ML-DSA KATs if randomized signing is not enabled
if sig_name in ["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"] and not helpers.is_ml_dsa_randomized_signing_enabled():
pytest.skip("Skipping ML-DSA KAT signature test: implementation built in deterministic mode")
if not(helpers.is_sig_enabled_by_name(sig_name)): pytest.skip('Not enabled')
output = helpers.run_subprocess(
[helpers.path_to_executable('kat_sig'), sig_name],
Expand Down
3 changes: 3 additions & 0 deletions tests/test_kat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def test_sig(sig_name):
kats = helpers.get_kats("sig")
# slh dsa will run ACVP vectors instead
if ("SLH_DSA" in sig_name): pytest.skip('slhdsa not enabled for KATs')
# Skip ML-DSA KATs if randomized signing is not enabled
if sig_name in ["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"] and not helpers.is_ml_dsa_randomized_signing_enabled():
pytest.skip("Skipping ML-DSA KAT signature test: implementation built in deterministic mode")
if not(helpers.is_sig_enabled_by_name(sig_name)): pytest.skip('Not enabled')
output = helpers.run_subprocess(
[helpers.path_to_executable('kat_sig'), sig_name, '--all'],
Expand Down
105 changes: 105 additions & 0 deletions tests/test_sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,101 @@ static OQS_STATUS sig_test_correctness(const char *method_name, bool bitflips_al
return ret;
}

#if defined(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
// Test that two signatures of the same message are different.
static OQS_STATUS sig_test_randomized_signing(const char *method_name) {
OQS_SIG *sig = NULL;
uint8_t *public_key = NULL;
uint8_t *secret_key = NULL;
uint8_t *message = NULL;
size_t message_len = 100;
uint8_t *signature1 = NULL;
size_t signature1_len;
uint8_t *signature2 = NULL;
size_t signature2_len;
OQS_STATUS rc, ret = OQS_ERROR;

sig = OQS_SIG_new(method_name);
if (sig == NULL) {
fprintf(stderr, "ERROR: OQS_SIG_new failed for %s\n", method_name);
return OQS_ERROR;
}

printf("Testing randomized signing for %s\n", sig->method_name);

public_key = OQS_MEM_malloc(sig->length_public_key);
secret_key = OQS_MEM_malloc(sig->length_secret_key);
message = OQS_MEM_malloc(message_len);
signature1 = OQS_MEM_malloc(sig->length_signature);
signature2 = OQS_MEM_malloc(sig->length_signature);

if ((public_key == NULL) || (secret_key == NULL) || (message == NULL) || (signature1 == NULL) || (signature2 == NULL)) {
fprintf(stderr, "ERROR: OQS_MEM_malloc failed\n");
goto err;
}

OQS_randombytes(message, message_len);

rc = OQS_SIG_keypair(sig, public_key, secret_key);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_keypair failed\n");
goto err;
}

rc = OQS_SIG_sign(sig, signature1, &signature1_len, message, message_len, secret_key);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_sign failed\n");
goto err;
}

rc = OQS_SIG_sign(sig, signature2, &signature2_len, message, message_len, secret_key);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_sign failed\n");
goto err;
}

if (signature1_len != signature2_len) {
printf("Signatures have different lengths, so they are not identical.\n");
} else if (memcmp(signature1, signature2, signature1_len) == 0) {
fprintf(stderr, "ERROR: Two signatures of the same message are identical.\n");
goto err;
} else {
printf("Two signatures of the same message are not identical, as expected.\n");
}

rc = OQS_SIG_verify(sig, message, message_len, signature1, signature1_len, public_key);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_verify failed for signature 1\n");
goto err;
}

rc = OQS_SIG_verify(sig, message, message_len, signature2, signature2_len, public_key);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_verify failed for signature 2\n");
goto err;
}

printf("verification passes as expected\n");
ret = OQS_SUCCESS;
goto cleanup;

err:
ret = OQS_ERROR;

cleanup:
if (secret_key) {
OQS_MEM_secure_free(secret_key, sig->length_secret_key);
}
OQS_MEM_insecure_free(public_key);
OQS_MEM_insecure_free(message);
OQS_MEM_insecure_free(signature1);
OQS_MEM_insecure_free(signature2);
OQS_SIG_free(sig);

return ret;
}
#endif

#ifdef OQS_ENABLE_TEST_CONSTANT_TIME
static void TEST_SIG_randombytes(uint8_t *random_array, size_t bytes_to_read) {
// We can't make direct calls to the system randombytes on some platforms,
Expand Down Expand Up @@ -349,6 +444,16 @@ int main(int argc, char **argv) {
}
#endif

#if defined(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
if (strncmp(alg_name, "ML-DSA", 6) == 0) {
rc = sig_test_randomized_signing(alg_name);
if (rc != OQS_SUCCESS) {
OQS_destroy();
return EXIT_FAILURE;
}
}
#endif

#if OQS_USE_PTHREADS && !defined(OQS_ENABLE_TEST_CONSTANT_TIME)
#define MAX_LEN_SIG_NAME_ 64
// don't run algorithms with large stack usage in threads
Expand Down
Loading