Skip to content

Commit 71a65ef

Browse files
authored
Replace SPHINCS+ with SLH-DSA for OQS_ALGS_ENABLED=STD (#2290)
* can pass slh_dsa schemes to copy_from_upstream Signed-off-by: Ganyu (Bruce) Xu <[email protected]> * hacked copy_from_slh_dsa_c to extract SLH-DSA schemes, implemented replace_one_fragment so list_standardized_algs.fragment can be rendered with a separate set of instructions Signed-off-by: Ganyu (Bruce) Xu <[email protected]> * Exclude pre-hashed SLH-DSA variants from OQS_ALGS_ENABLED=STD Signed-off-by: Ganyu (Bruce) Xu <[email protected]> --------- Signed-off-by: Ganyu (Bruce) Xu <[email protected]>
1 parent 9a61d90 commit 71a65ef

File tree

4 files changed

+85
-5
lines changed

4 files changed

+85
-5
lines changed

.CMake/alg_support.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ if(NOT ((OQS_MINIMAL_BUILD STREQUAL "") OR (OQS_MINIMAL_BUILD STREQUAL "OFF")))
433433
filter_algs("${OQS_MINIMAL_BUILD}")
434434
elseif (${OQS_ALGS_ENABLED} STREQUAL "STD")
435435
##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_LIST_STANDARDIZED_ALGS_START
436-
filter_algs("KEM_ml_kem_512;KEM_ml_kem_768;KEM_ml_kem_1024;SIG_ml_dsa_44;SIG_ml_dsa_65;SIG_ml_dsa_87;SIG_falcon_512;SIG_falcon_1024;SIG_falcon_padded_512;SIG_falcon_padded_1024;SIG_sphincs_sha2_128f_simple;SIG_sphincs_sha2_128s_simple;SIG_sphincs_sha2_192f_simple;SIG_sphincs_sha2_192s_simple;SIG_sphincs_sha2_256f_simple;SIG_sphincs_sha2_256s_simple;SIG_sphincs_shake_128f_simple;SIG_sphincs_shake_128s_simple;SIG_sphincs_shake_192f_simple;SIG_sphincs_shake_192s_simple;SIG_sphincs_shake_256f_simple;SIG_sphincs_shake_256s_simple")
436+
filter_algs("KEM_ml_kem_512;KEM_ml_kem_768;KEM_ml_kem_1024;SIG_ml_dsa_44;SIG_ml_dsa_65;SIG_ml_dsa_87;SIG_falcon_512;SIG_falcon_1024;SIG_falcon_padded_512;SIG_falcon_padded_1024;SIG_slh_dsa_pure_sha2_128s;SIG_slh_dsa_pure_sha2_128f;SIG_slh_dsa_pure_sha2_192s;SIG_slh_dsa_pure_sha2_192f;SIG_slh_dsa_pure_sha2_256s;SIG_slh_dsa_pure_sha2_256f;SIG_slh_dsa_pure_shake_128s;SIG_slh_dsa_pure_shake_128f;SIG_slh_dsa_pure_shake_192s;SIG_slh_dsa_pure_shake_192f;SIG_slh_dsa_pure_shake_256s;SIG_slh_dsa_pure_shake_256f")
437437
##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_LIST_STANDARDIZED_ALGS_END
438438
elseif(${OQS_ALGS_ENABLED} STREQUAL "NIST_R4")
439439
filter_algs("KEM_classic_mceliece_348864;KEM_classic_mceliece_348864f;KEM_classic_mceliece_460896;KEM_classic_mceliece_460896f;KEM_classic_mceliece_6688128;KEM_classic_mceliece_6688128f;KEM_classic_mceliece_6960119;KEM_classic_mceliece_6960119f;KEM_classic_mceliece_8192128;KEM_classic_mceliece_8192128f;KEM_hqc_128;KEM_hqc_192;KEM_hqc_256;KEM_bike_l1;KEM_bike_l3;KEM_bike_l5")

scripts/copy_from_upstream/.CMake/alg_support.cmake/list_standardized_algs.fragment

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{%- if 'alias_scheme' in scheme -%}KEM_{{ family['name'] }}_{{ scheme['alias_scheme'] }}{%- else -%}KEM_{{ family['name'] }}_{{ scheme['scheme'] }}{%- endif -%};
66
{%- endfor -%}
77
{%- endfor -%}
8-
{%- for family in instructions['sigs'] if family['name'] in ['ml_dsa', 'falcon', 'sphincs'] -%}
8+
{%- for family in instructions['sigs'] if family['name'] in ['ml_dsa', 'falcon', 'slh_dsa'] -%}
99
{%- set outer_loop = loop -%}
1010
{%- for scheme in family['schemes'] -%}
1111
{%- if 'alias_scheme' in scheme -%}SIG_{{ family['name'] }}_{{ scheme['alias_scheme'] }}{%- else -%}SIG_{{ family['name'] }}_{{ scheme['scheme'] }}{%- endif -%}{%- if not (outer_loop.last and loop.last) -%};{%- endif -%}

scripts/copy_from_upstream/copy_from_slh_dsa_c.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,5 +337,18 @@ def main():
337337
# apply patches
338338
apply_patches(slh_patch_dir)
339339

340+
# NOTE: from [issue 2203](https://github.com/open-quantum-safe/liboqs/issues/2203)
341+
# SLH-DSA is not described in copy_from_upstream.yml. It is instead described
342+
# here in this separate module. This makes replacing SPHINCS+ with SLH-DSA
343+
# in list_standardized_algs.fragment non-trivial because this Jinja template
344+
# is rendered from copy_from_upstream.yml.
345+
# As a necessary hack, the list of variants (e.g. "pure_sha2_128s") is returned
346+
# so that copy_from_upstream.py can use this list to construct a dictionary
347+
# that resembles the structure of copy_from_upstream.yml.
348+
# In the near future I want to consider refactoring build configuration
349+
# management and upstream integration scripts. The status quo is a mess and
350+
# will make future integrations all the more difficult.
351+
return variants
352+
340353
if __name__ == "__main__":
341354
main()

scripts/copy_from_upstream/copy_from_upstream.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import platform
1717
import update_upstream_alg_docs
1818
import copy_from_slh_dsa_c
19+
from copy import deepcopy
1920

2021
# kats of all algs
2122
kats = {}
@@ -97,6 +98,46 @@ def generator_all(filename, instructions):
9798
contents = jinja2.Template(template).render({'instructions': instructions})
9899
file_put_contents(filename, contents)
99100

101+
# TODO: consider refactoring replacer by calling replace_one_fragment
102+
def replace_one_fragment(
103+
dst_path: str,
104+
template_path: str,
105+
instructions: dict,
106+
delimiter: str,
107+
libjade: bool = False,
108+
):
109+
"""Replace a single fragment with a rendered Jinja template
110+
111+
:param dst_path: path to the rendered file, relative to LIBOQS_DIR
112+
:param template_path: path to the Jinja template file, relative to LIBOQS_DIR
113+
:param instructions: copy_from_upstream.yml or some patched version
114+
:param delimiter: how the identifer for the fragment in the destination file
115+
is prefixed
116+
"""
117+
liboqs_dir = os.environ.get("LIBOQS_DIR", None)
118+
if not liboqs_dir:
119+
raise KeyError("Environment variable LIBOQS_DIR is missing")
120+
dst_path = os.path.join(liboqs_dir, dst_path)
121+
template_path = os.path.join(liboqs_dir, template_path)
122+
with open(template_path, "r") as template_f, open(dst_path, "r") as dst_f:
123+
template = template_f.read()
124+
dst_content = dst_f.read()
125+
identifier, _ = os.path.splitext(os.path.basename(template_path))
126+
jade_or_upstream = "LIBJADE" if libjade else "UPSTREAM"
127+
identifier_start = f"{delimiter} OQS_COPY_FROM_{jade_or_upstream}_FRAGMENT_{identifier.upper()}_START"
128+
identifier_end = f"{delimiter} OQS_COPY_FROM_{jade_or_upstream}_FRAGMENT_{identifier.upper()}_END"
129+
preamble = dst_content[: dst_content.find(identifier_start)]
130+
postamble = dst_content[dst_content.find(identifier_end) :]
131+
dst_content = (
132+
preamble
133+
+ identifier_start
134+
+ jinja2.Template(template).render(
135+
{"instructions": instructions, "non_upstream_kems": non_upstream_kems}
136+
)
137+
+ postamble
138+
)
139+
with open(dst_path, "w") as f:
140+
f.write(dst_content)
100141

101142
def replacer(filename, instructions, delimiter, libjade=False):
102143
fragments = glob.glob(
@@ -701,14 +742,29 @@ def process_families(instructions, basedir, with_kat, with_generator, with_libja
701742
)
702743

703744

704-
def copy_from_upstream():
745+
def copy_from_upstream(slh_dsa_inst: dict):
746+
"""Integrate upstreams implementations and algorithms described in
747+
copy_from_upstream.yml.
748+
749+
:param slh_dsa_inst: instruction for integrating SLH-DSA, only used for
750+
rendering alg_support.cmake
751+
"""
705752
for t in ["kem", "sig"]:
706753
with open(os.path.join(os.environ['LIBOQS_DIR'], 'tests', 'KATs', t, 'kats.json'), 'r') as fp:
707754
kats[t] = json.load(fp)
708755

709756
instructions = load_instructions('copy_from_upstream.yml')
757+
patched_inst: dict = deepcopy(instructions)
758+
patched_inst["sigs"].append(slh_dsa_inst["sigs"][0])
710759
process_families(instructions, os.environ['LIBOQS_DIR'], True, True)
711760
replacer('.CMake/alg_support.cmake', instructions, '#####')
761+
# NOTE: issue 2203, only for replacing list of standardized algs
762+
replace_one_fragment(
763+
".CMake/alg_support.cmake",
764+
"scripts/copy_from_upstream/.CMake/alg_support.cmake/list_standardized_algs.fragment",
765+
patched_inst,
766+
"#####"
767+
)
712768
replacer('CMakeLists.txt', instructions, '#####')
713769
replacer('src/oqsconfig.h.cmake', instructions, '/////')
714770
replacer('src/CMakeLists.txt', instructions, '#####')
@@ -839,9 +895,20 @@ def verify_from_upstream():
839895

840896
if args.operation == "copy":
841897
# copy_from_slh_dsa_c will modify slh_dsa.yml before copy_from_upstream modifies md files
842-
copy_from_slh_dsa_c.main()
898+
slh_dsa_schemes: list[str] = copy_from_slh_dsa_c.main()
899+
slh_dsa_instruction = {
900+
"sigs": [
901+
{
902+
"name": "slh_dsa",
903+
"schemes": [
904+
{"scheme": scheme} for scheme in slh_dsa_schemes
905+
if "pure" in scheme
906+
]
907+
}
908+
]
909+
}
843910
os.chdir(os.path.join(os.environ['LIBOQS_DIR'],"scripts","copy_from_upstream"))
844-
copy_from_upstream()
911+
copy_from_upstream(slh_dsa_instruction)
845912
elif args.operation == "libjade":
846913
copy_from_libjade()
847914
elif args.operation == "verify":

0 commit comments

Comments
 (0)