Skip to content
Open
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
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: 8 additions & 0 deletions CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ For a full list of such options and their default values, consult [.CMake/alg_su

**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

A selected algorithm set is enabled. Possible values are "STD" selecting all algorithms standardized by NIST; "NIST_R4" selecting all algorithms evaluated in round 4 of the NIST PQC competition; "NIST_SIG_ONRAMP" selecting algorithms evaluated in the NIST PQC "onramp" standardization for additional signature schemes; "All" (or any other value) selecting all algorithms integrated into liboqs. Parameter setting "STD" minimizes library size but may require re-running code generator scripts in projects integrating `liboqs`; e.g., [oqs-provider](https://github.com/open-quantum-safe/oqs-provider) and [oqs-boringssl](https://github.com/open-quantum-safe/boringssl).
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
115 changes: 115 additions & 0 deletions tests/test_sig.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,110 @@ static OQS_STATUS sig_test_correctness(const char *method_name, bool bitflips_al
return ret;
}

static OQS_STATUS sig_test_repeat_signing(const char *method_name, bool is_randomized) {
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 %s signing for %s\n", is_randomized ? "randomized" : "deterministic", 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) {
fprintf(stderr, "ERROR: %s signing for %s produced signatures of different lengths (%zu vs %zu). This should not happen.\n",
is_randomized ? "Randomized" : "Deterministic", sig->method_name, signature1_len, signature2_len);
goto err;
}

if (is_randomized) {
if (memcmp(signature1, signature2, signature1_len) == 0) {
fprintf(stderr, "ERROR: Two signatures of the same message are identical in randomized mode.\n");
goto err;
} else {
printf("Two signatures of the same message are not identical, as expected for randomized mode.\n");
}
} else {
if (memcmp(signature1, signature2, signature1_len) != 0) {
fprintf(stderr, "ERROR: Two signatures of the same message are NOT identical in deterministic mode.\n");
goto err;
} else {
printf("Two signatures of the same message are identical, as expected for deterministic mode.\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;
}
#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 +453,17 @@ int main(int argc, char **argv) {
}
#endif

if (strncmp(alg_name, "ML-DSA", 6) == 0) {
#if defined(OQS_ENABLE_SIG_ML_DSA_RANDOMIZED_SIGNING)
rc = sig_test_repeat_signing(alg_name, true);
#else
rc = sig_test_repeat_signing(alg_name, false);
#endif
if (rc != OQS_SUCCESS) {
OQS_destroy();
return EXIT_FAILURE;
}
}
#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