diff --git a/.circleci/config.yml b/.circleci/config.yml index 5879827fe..0601b4563 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -249,6 +249,7 @@ workflows: context: openquantumsafe CONTAINER: openquantumsafe/ci-ubuntu-bionic-x86_64:latest CMAKE_ARGS: -DCMAKE_C_COMPILER=clang-9 -DCMAKE_BUILD_TYPE=Debug -DUSE_SANITIZER=Address + PYTEST_ARGS: --ignore=tests/test_portability.py --numprocesses=auto # Disabling for now due to https://github.com/open-quantum-safe/liboqs/issues/791 #- linux_x64: # name: undefined-sanitizer diff --git a/README.md b/README.md index f2e94516e..5d82be96d 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,8 @@ The following instructions assume we are in `build`. - `test_kem`: Simple test harness for key encapsulation mechanisms - `test_sig`: Simple test harness for key signature schemes + - `test_kem_mem`: Simple test harness for checking memory consumption of key encapsulation mechanisms + - `test_sig_mem`: Simple test harness for checking memory consumption of key signature schemes - `kat_kem`: Program that generates known answer test (KAT) values for key encapsulation mechanisms using the same procedure as the NIST submission requirements, for checking against submitted KAT values using `tests/test_kat.py` - `kat_sig`: Program that generates known answer test (KAT) values for signature schemes using the same procedure as the NIST submission requirements, for checking against submitted KAT values using `tests/test_kat.py` - `speed_kem`: Benchmarking program for key encapsulation mechanisms; see `./speed_kem --help` for usage instructions @@ -119,6 +121,7 @@ The following instructions assume we are in `build`. - `example_kem`: Minimal runnable example showing the usage of the KEM API - `example_sig`: Minimal runnable example showing the usage of the signature API - `test_aes`, `test_sha3`: Simple test harnesses for crypto sub-components + - `test_portability`: Simple test harnesses for checking cross-CPU code portability; requires presence of `qemu`; proper operation validated only on Ubuntu The test suite can be run using diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bfa43bae8..400399742 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,7 +65,8 @@ add_library(oqs kem/kem.c ${KEM_OBJS} sig/sig.c ${SIG_OBJS} - $) + ${COMMON_OBJS}) +set(COMMON_OBJS ${COMMON_OBJS} PARENT_SCOPE) if(DEFINED SANITIZER_LD_FLAGS) target_link_libraries(oqs PUBLIC ${SANITIZER_LD_FLAGS}) endif() diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2fc513cd3..bce6a1465 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -49,10 +49,18 @@ endif() # That being said, code that requires these functions must take care to ensure # that they fall-back appropriately. +# What must be ensured, though, is that only sha3x4 is compiled with avx2; +# all other code must be compiled without this extension if portability is requested if(OQS_USE_AVX2_INSTRUCTIONS) +if(OQS_PORTABLE_BUILD) + add_library(sha3x4_avx2 OBJECT sha3/sha3x4.c) + target_compile_options(sha3x4_avx2 PRIVATE -mavx2) + set(_COMMON_OBJS $) +else() set(SHA3_IMPL ${SHA3_IMPL} sha3/sha3x4.c) add_compile_options(-mavx2) endif() +endif() add_library(common OBJECT ${AES_IMPL} ${SHA2_IMPL} @@ -64,3 +72,6 @@ add_library(common OBJECT ${AES_IMPL} if(OQS_USE_OPENSSL) target_include_directories(common PRIVATE ${OPENSSL_INCLUDE_DIR}) endif() + +set(_COMMON_OBJS ${_COMMON_OBJS} $) +set(COMMON_OBJS ${_COMMON_OBJS} PARENT_SCOPE) diff --git a/src/common/common.c b/src/common/common.c index 3ebe24d0d..596ac2a39 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -10,7 +10,7 @@ #include #endif -#if (defined(OQS_USE_CPU_EXTENSIONS) && defined(OQS_PORTABLE_BUILD)) +#if defined(OQS_USE_CPU_EXTENSIONS) static OQS_CPU_EXTENSIONS available_cpu_extensions = { 0 }; static unsigned int available_cpu_extensions_set = 0; @@ -148,10 +148,10 @@ OQS_API OQS_CPU_EXTENSIONS OQS_get_available_CPU_extensions(void) { } return available_cpu_extensions; } -#endif /* OQS_USE_CPU_EXTENSIONS && OQS_PORTABLE_BUILD */ +#endif /* OQS_USE_CPU_EXTENSIONS */ OQS_API void OQS_init(void) { -#if (defined(OQS_USE_CPU_EXTENSIONS) && defined(OQS_PORTABLE_BUILD)) +#if defined(OQS_USE_CPU_EXTENSIONS) if (!available_cpu_extensions_set) { #if defined(ARCH_X86_64) set_available_cpu_extensions_x86_64(); @@ -160,7 +160,7 @@ OQS_API void OQS_init(void) { #endif /* ARCH_X86_64 or ARCH_ARM_ANY */ available_cpu_extensions_set = 1; } -#endif /* OQS_USE_CPU_EXTENSIONS && OQS_PORTABLE_BUILD */ +#endif /* OQS_USE_CPU_EXTENSIONS */ } OQS_API void OQS_MEM_cleanse(void *ptr, size_t len) { diff --git a/src/common/common.h b/src/common/common.h index 1d53905ad..d5a47f82b 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -98,7 +98,7 @@ typedef enum { OQS_EXTERNAL_LIB_ERROR_OPENSSL = 50, } OQS_STATUS; -#if (defined(OQS_USE_CPU_EXTENSIONS) && defined(OQS_PORTABLE_BUILD)) +#if defined(OQS_USE_CPU_EXTENSIONS) /** * Architecture macros. @@ -147,7 +147,7 @@ OQS_API OQS_CPU_EXTENSIONS OQS_get_available_CPU_extensions(void); */ OQS_API const char *OQS_get_cpu_extension_name(unsigned int i); -#endif /* OQS_USE_CPU_EXTENSIONS && OQS_PORTABLE_BUILD */ +#endif /* OQS_USE_CPU_EXTENSIONS */ /** * This currently only sets the values in the OQS_CPU_EXTENSIONS, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2214e2a3f..ffd6d67fe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,13 +34,13 @@ if(NOT WIN32) execute_process(COMMAND ${PROJECT_SOURCE_DIR}/scripts/git_commit.sh OUTPUT_VARIABLE GIT_COMMIT) add_definitions(-DOQS_COMPILE_GIT_COMMIT="${GIT_COMMIT}") - add_executable(test_aes test_aes.c $) + add_executable(test_aes test_aes.c ${COMMON_OBJS}) target_link_libraries(test_aes PRIVATE ${INTERNAL_TEST_DEPS}) - add_executable(test_hash test_hash.c $) + add_executable(test_hash test_hash.c ${COMMON_OBJS}) target_link_libraries(test_hash PRIVATE ${INTERNAL_TEST_DEPS}) - add_executable(test_sha3 test_sha3.c $) + add_executable(test_sha3 test_sha3.c ${COMMON_OBJS}) target_link_libraries(test_sha3 PRIVATE ${INTERNAL_TEST_DEPS}) set(UNIX_TESTS test_aes test_hash test_sha3) diff --git a/tests/system_info.c b/tests/system_info.c index e68c0fa1c..4a89eb542 100644 --- a/tests/system_info.c +++ b/tests/system_info.c @@ -60,7 +60,7 @@ static void print_platform_info(void) { /* Display all active CPU extensions: */ static void print_cpu_extensions(void) { -#if defined(OQS_USE_CPU_EXTENSIONS) && defined(OQS_PORTABLE_BUILD) +#if defined(OQS_USE_CPU_EXTENSIONS) /* Make CPU features struct iterable */ typedef union ext_u { OQS_CPU_EXTENSIONS ext_x; @@ -81,10 +81,15 @@ static void print_cpu_extensions(void) { } } } - printf("\n"); #else /* no extensions active */ - printf("CPU exts active: None\n"); + printf("CPU exts active: None"); #endif +#if defined(OQS_PORTABLE_BUILD) + printf(" (portable build)\n"); +#else + printf("\n"); +#endif + } static void print_oqs_configuration(void) { diff --git a/tests/test_portability.py b/tests/test_portability.py new file mode 100644 index 000000000..5ee74b980 --- /dev/null +++ b/tests/test_portability.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: MIT + +import helpers +import pytest +import platform +from pathlib import Path + +MIN_CPUS = {} +# set other CPU types for other architectures; Westmere supports cpuid but not avx2 +MIN_CPUS["x86_64"] = "Westmere" + +@helpers.filtered_test +@pytest.mark.parametrize('kem_name', helpers.available_kems_by_name()) +@pytest.mark.skipif(not "Ubuntu" in platform.platform(), reason="Only supported on Ubuntu") +def test_kem(kem_name): + if not(helpers.is_build_portable()): + pytest.skip("Portability not enabled") + + if not(helpers.is_kem_enabled_by_name(kem_name)): + pytest.skip('Not enabled') + + helpers.run_subprocess(["qemu-"+platform.machine(), "-cpu", MIN_CPUS[platform.machine()], + helpers.path_to_executable('test_kem'), kem_name]) + +@helpers.filtered_test +@pytest.mark.parametrize('sig_name', helpers.available_sigs_by_name()) +@pytest.mark.skipif(not "Ubuntu" in platform.platform(), reason="Only supported on Ubuntu") +def test_sig(sig_name): + if not(helpers.is_build_portable()): + pytest.skip("Portability not enabled") + + if not(helpers.is_sig_enabled_by_name(sig_name)): + pytest.skip('Not enabled') + + if (sig_name.startswith("picnic")): + pytest.skip("Picnic portability known to not be given.") + + helpers.run_subprocess(["qemu-"+platform.machine(), "-cpu", MIN_CPUS[platform.machine()], + helpers.path_to_executable('test_sig'), sig_name]) + +if __name__ == "__main__": + import sys + pytest.main(sys.argv) +