From 4906c3fc880328453a9efccde860e11adc5fea29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 20 Dec 2023 08:20:56 +0100 Subject: [PATCH] Add support for embedded Zephyr RTOS (#1621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Zephyr RTOS support This commit adds initial support for the zephyr operating system. Some minor changes to the library build system have been made for it to be compilable with zephyr. Furthermore, we added support for an embedded build option to disable standard library methods for random number generation. * Zephyr: added algorithm selection The algorithms can now be selected with Kconfig. Per default, we only enable the algorithms selected by NIST to be standardized. However, all supported algorithms can be enabled or disabled individually on a per project basis. * Zephyr: added testable samples Added two sample applications within the zephyr directory for KEMs and Signatures. These are also intended for CI testing. * Zephyr: added CI tests * Zephyr: Add documentation Signed-off-by: Tobias Frauenschläger --- .CMake/compiler_opts.cmake | 2 +- .github/workflows/zephyr.yml | 46 +++++ CMakeLists.txt | 1 + CONFIGURE.md | 9 + src/CMakeLists.txt | 6 +- src/common/rand/rand.c | 33 ++-- src/common/rand/rand.h | 8 +- src/oqsconfig.h.cmake | 2 + zephyr/CMakeLists.txt | 153 ++++++++++++++++ zephyr/Kconfig | 59 +++++++ zephyr/README.md | 54 ++++++ zephyr/module.yml | 4 + zephyr/samples/KEMs/CMakeLists.txt | 14 ++ zephyr/samples/KEMs/prj.conf | 23 +++ zephyr/samples/KEMs/sample.yaml | 16 ++ zephyr/samples/KEMs/src/main.c | 213 +++++++++++++++++++++++ zephyr/samples/Signatures/CMakeLists.txt | 13 ++ zephyr/samples/Signatures/prj.conf | 16 ++ zephyr/samples/Signatures/sample.yaml | 16 ++ zephyr/samples/Signatures/src/main.c | 191 ++++++++++++++++++++ 20 files changed, 858 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/zephyr.yml create mode 100644 zephyr/CMakeLists.txt create mode 100644 zephyr/Kconfig create mode 100644 zephyr/README.md create mode 100644 zephyr/module.yml create mode 100644 zephyr/samples/KEMs/CMakeLists.txt create mode 100644 zephyr/samples/KEMs/prj.conf create mode 100644 zephyr/samples/KEMs/sample.yaml create mode 100644 zephyr/samples/KEMs/src/main.c create mode 100644 zephyr/samples/Signatures/CMakeLists.txt create mode 100644 zephyr/samples/Signatures/prj.conf create mode 100644 zephyr/samples/Signatures/sample.yaml create mode 100644 zephyr/samples/Signatures/src/main.c diff --git a/.CMake/compiler_opts.cmake b/.CMake/compiler_opts.cmake index 98fe0b72b7..083529b32c 100644 --- a/.CMake/compiler_opts.cmake +++ b/.CMake/compiler_opts.cmake @@ -85,7 +85,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") add_compile_options(${OQS_OPT_FLAG}) # If this is not a dist build we also need to set the OQS_USE_[EXTENSION] flags - if(NOT ${OQS_DIST_BUILD}) + if(NOT ${OQS_DIST_BUILD} AND NOT CMAKE_CROSSCOMPILING) include(${CMAKE_CURRENT_LIST_DIR}/gcc_clang_intrinsics.cmake) endif() endif() diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml new file mode 100644 index 0000000000..9f8d4a24e8 --- /dev/null +++ b/.github/workflows/zephyr.yml @@ -0,0 +1,46 @@ +name: Zephyr tests + +on: [push, pull_request] + +jobs: + + zephyr_test: + runs-on: ubuntu-22.04 + container: ghcr.io/zephyrproject-rtos/ci:latest + env: + CMAKE_PREFIX_PATH: /opt/toolchains + strategy: + fail-fast: false + matrix: + config: + - zephyr-ref: v3.4.0 + - zephyr-ref: v3.5.0 + + steps: + - name: Init west workspace + run: west init --mr ${{ matrix.config.zephyr-ref }} zephyr + + - name: Update west.yml + working-directory: zephyr/zephyr + run: | + REF=$(echo '${{ github.ref }}' | sed -e 's/\//\\\//g') + sed -e 's/remotes:/remotes:\n \- name: liboqs\n url\-base: https:\/\/github.com\/${{ github.repository_owner }}/' -i west.yml + sed -e "s/projects:/projects:\n \- name: liboqs\n path: modules\/crypto\/liboqs\n remote: liboqs\n revision: $REF/" -i west.yml + + - name: Update west workspace + working-directory: zephyr + run: west update -n -o=--depth=1 + + - name: Export zephyr + working-directory: zephyr + run: west zephyr-export + + - name: Run Signature test + working-directory: zephyr + run: | + west twister --integration -T modules/crypto/liboqs/zephyr -s samples/Signatures/sample.crypto.liboqs_signature_example -vvv + + - name: Run KEM test + working-directory: zephyr + run: | + west twister --integration -T modules/crypto/liboqs/zephyr -s samples/KEMs/sample.crypto.liboqs_kem_example -vvv diff --git a/CMakeLists.txt b/CMakeLists.txt index 03912599d1..1694e4ec83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ option(OQS_BUILD_ONLY_LIB "Build only liboqs and do not expose build targets for set(OQS_MINIMAL_BUILD "" CACHE STRING "Only build specifically listed algorithms.") option(OQS_PERMIT_UNSUPPORTED_ARCHITECTURE "Permit compilation on an an unsupported architecture." OFF) option(OQS_STRICT_WARNINGS "Enable all compiler warnings." OFF) +option(OQS_EMBEDDED_BUILD "Compile liboqs for an Embedded environment without a full standard library." OFF) set(OQS_OPT_TARGET auto CACHE STRING "The target microarchitecture for optimization.") diff --git a/CONFIGURE.md b/CONFIGURE.md index d5407ca5ff..dd5a15ccfa 100644 --- a/CONFIGURE.md +++ b/CONFIGURE.md @@ -18,6 +18,7 @@ The following options can be passed to CMake before the build file generation pr - [USE_SANITIZER](#USE_SANITIZER) - [OQS_ENABLE_TEST_CONSTANT_TIME](#OQS_ENABLE_TEST_CONSTANT_TIME) - [OQS_STRICT_WARNINGS](#OQS_STRICT_WARNINGS) +- [OQS_EMBEDDED_BUILD](#OQS_EMBEDDED_BUILD) ## BUILD_SHARED_LIBS @@ -155,4 +156,12 @@ Can be `ON` or `OFF`. When `ON`, all compiler warnings are enabled and treated a **Default**: `OFF`. +## OQS_EMBEDDED_BUILD +Can be `ON` or `OFF`. When `ON`, calls to standard library functions typically not present in a bare-metal embedded environment are excluded from compilation. + +At the moment, this is **only** considered for random number generation, as both `getentropy()` and a file based `/dev/urandom` are not available on embedded targets (e.g. the Zephyr port). + +**Attention**: When this option is enabled, you have to supply a custom callback for obtaining random numbers using the `OQS_randombytes_custom_algorithm()` API before accessing the cryptographic API. Otherwise, all key generation and signing operations will fail. + +**Default**: `OFF`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e31a27f61c..6d867097fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,12 +70,12 @@ target_include_directories(oqs ) set_target_properties(oqs PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" VERSION ${OQS_VERSION_TEXT} SOVERSION 5 # For Windows DLLs - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/liboqsConfig.cmake" diff --git a/src/common/rand/rand.c b/src/common/rand/rand.c index 92ea271d19..fd5b9d0022 100644 --- a/src/common/rand/rand.c +++ b/src/common/rand/rand.c @@ -57,13 +57,27 @@ OQS_API void OQS_randombytes(uint8_t *random_array, size_t bytes_to_read) { oqs_randombytes_algorithm(random_array, bytes_to_read); } -#if !defined(_WIN32) -#if defined(__APPLE__) +// Select the implementation for OQS_randombytes_system +#if defined(_WIN32) +void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { + HCRYPTPROV hCryptProv; + if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) || + !CryptGenRandom(hCryptProv, (DWORD) bytes_to_read, random_array)) { + exit(EXIT_FAILURE); // better to fail than to return bad random data + } + CryptReleaseContext(hCryptProv, 0); +} +#elif defined(__APPLE__) void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { arc4random_buf(random_array, bytes_to_read); } -#else -#if defined(OQS_HAVE_GETENTROPY) +#elif defined(OQS_EMBEDDED_BUILD) +void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { + fprintf(stderr, "OQS_randombytes_system is not available in an embedded build.\n"); + fprintf(stderr, "Call OQS_randombytes_custom_algorithm() to set a custom method for your system.\n"); + exit(EXIT_FAILURE); +} +#elif defined(OQS_HAVE_GETENTROPY) void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { while (bytes_to_read > 256) { if (getentropy(random_array, 256)) { @@ -96,17 +110,6 @@ void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { fclose(handle); } #endif -#endif -#else -void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read) { - HCRYPTPROV hCryptProv; - if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) || - !CryptGenRandom(hCryptProv, (DWORD) bytes_to_read, random_array)) { - exit(EXIT_FAILURE); // better to fail than to return bad random data - } - CryptReleaseContext(hCryptProv, 0); -} -#endif #ifdef OQS_USE_OPENSSL #define OQS_RAND_POLL_RETRY 3 // in case failure to get randomness is a temporary problem, allow some repeats diff --git a/src/common/rand/rand.h b/src/common/rand/rand.h index 3499c2593d..61ccbebd1f 100644 --- a/src/common/rand/rand.h +++ b/src/common/rand/rand.h @@ -28,6 +28,10 @@ extern "C" { /** * Switches OQS_randombytes to use the specified algorithm. * + * @warning In case you have set a custom algorithm using `OQS_randombytes_custom_algorithm` + * before, this function will overwrite it again. Hence, you have to set your custom + * algorithm again after calling this function. + * * @param[in] algorithm The name of the algorithm to use. * @return OQS_SUCCESS if `algorithm` is a supported algorithm name, OQS_ERROR otherwise. */ @@ -36,7 +40,7 @@ OQS_API OQS_STATUS OQS_randombytes_switch_algorithm(const char *algorithm); /** * Switches OQS_randombytes to use the given function. * - * This allows additional custom RNGs besides the provided ones. The provided RNG + * This allows additional custom RNGs besides the provided ones. The provided RNG * function must have the same signature as `OQS_randombytes`. * * @param[in] algorithm_ptr Pointer to the RNG function to use. @@ -48,7 +52,7 @@ OQS_API void OQS_randombytes_custom_algorithm(void (*algorithm_ptr)(uint8_t *, s * * This implementation uses whichever algorithm has been selected by * OQS_randombytes_switch_algorithm. The default is OQS_randombytes_system, which - * reads bytes directly from `/dev/urandom`. + * reads bytes from a system specific default source. * * The caller is responsible for providing a buffer allocated with sufficient room. * diff --git a/src/oqsconfig.h.cmake b/src/oqsconfig.h.cmake index c68c068c28..e664c274b6 100644 --- a/src/oqsconfig.h.cmake +++ b/src/oqsconfig.h.cmake @@ -25,6 +25,8 @@ #cmakedefine OQS_USE_SHA2_OPENSSL 1 #cmakedefine OQS_USE_SHA3_OPENSSL 1 +#cmakedefine OQS_EMBEDDED_BUILD 1 + #cmakedefine OQS_USE_ADX_INSTRUCTIONS 1 #cmakedefine OQS_USE_AES_INSTRUCTIONS 1 #cmakedefine OQS_USE_AVX_INSTRUCTIONS 1 diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt new file mode 100644 index 0000000000..624b9beb9c --- /dev/null +++ b/zephyr/CMakeLists.txt @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: MIT + +# Only add liboqs Zephyr module if enabled in Kconfig +if(CONFIG_LIBOQS) + # Workarounds for Zephyr + if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") + # We have to set that manually as CMake can't detect it properly in Zephyr + set(CMAKE_SIZEOF_VOID_P 8) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") + # Workaround as the generic name "arm" is not a supported architecture in liboqs. + # In Zephyr, however, it is exclusively used for 32-bit ARM architectures. + set(CMAKE_SYSTEM_PROCESSOR "armv7") + + # We have to set that manually as CMake can't detect it properly in Zephyr + set(CMAKE_SIZEOF_VOID_P 4) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "posix") + # Workaround to enable the native Zephyr builds on the Linux host system. + if(BOARD MATCHES "native_posix|native_sim") + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) + else() + message(FATAL_ERROR "Unsupported board ${BOARD} with posix architecture") + endif() + + # We have to set that manually as CMake can't detect it properly in Zephyr + set(CMAKE_SIZEOF_VOID_P 8) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86") + # We have to set that manually as CMake can't detect it properly in Zephyr + set(CMAKE_SIZEOF_VOID_P 4) + endif() + + # Configuration for liboqs + set(OQS_DIST_BUILD OFF) + set(OQS_BUILD_ONLY_LIB ON) + set(OQS_USE_OPENSSL OFF) + set(OQS_EMBEDDED_BUILD ON) + + set(CMAKE_CROSSCOMPILING ON) + + # Disable features by hand, as CMake won't find them properly with Zephyr + set(CMAKE_HAVE_GETENTROPY OFF) + set(CMAKE_HAVE_ALIGNED_ALLOC OFF) + set(CMAKE_HAVE_POSIX_MEMALIGN OFF) + set(CMAKE_HAVE_MEMALIGN OFF) + set(CMAKE_HAVE_EXPLICIT_BZERO OFF) + set(CMAKE_HAVE_MEMSET_S OFF) + set(CC_SUPPORTS_WA_NOEXECSTACK OFF) + set(LD_SUPPORTS_WL_Z_NOEXECSTACK OFF) + + # Algorithm selection (based on Kconfig) + if(CONFIG_LIBOQS_ENABLE_KEM_BIKE) + set(OQS_ENABLE_KEM_BIKE ON) + else() + set(OQS_ENABLE_KEM_BIKE OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_KEM_FRODOKEM) + set(OQS_ENABLE_KEM_FRODOKEM ON) + else() + set(OQS_ENABLE_KEM_FRODOKEM OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_KEM_NTRUPRIME) + set(OQS_ENABLE_KEM_NTRUPRIME ON) + else() + set(OQS_ENABLE_KEM_NTRUPRIME OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_KEM_CLASSIC_MCELIECE) + set(OQS_ENABLE_KEM_CLASSIC_MCELIECE ON) + else() + set(OQS_ENABLE_KEM_CLASSIC_MCELIECE OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_KEM_HQC) + set(OQS_ENABLE_KEM_HQC ON) + else() + set(OQS_ENABLE_KEM_HQC OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_KEM_KYBER) + set(OQS_ENABLE_KEM_KYBER ON) + else() + set(OQS_ENABLE_KEM_KYBER OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_SIG_DILITHIUM) + set(OQS_ENABLE_SIG_DILITHIUM ON) + else() + set(OQS_ENABLE_SIG_DILITHIUM OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_SIG_FALCON) + set(OQS_ENABLE_SIG_FALCON ON) + else() + set(OQS_ENABLE_SIG_FALCON OFF) + endif() + + if(CONFIG_LIBOQS_ENABLE_SIG_SPHINCS) + set(OQS_ENABLE_SIG_SPHINCS ON) + else() + set(OQS_ENABLE_SIG_SPHINCS OFF) + endif() + + # Add the actual liboqs targets + add_subdirectory(.. build) + + # Add target specific options to all liboqs targets + zephyr_get_targets(.. "STATIC_LIBRARY;OBJECT_LIBRARY" ALL_TARGETS) + foreach(target ${ALL_TARGETS}) + # Zephyr include directories + target_include_directories(${target} PRIVATE + $ + ) + + # Zephyr system include directories + target_include_directories(${target} SYSTEM PRIVATE + $ + ) + + # Definitions + target_compile_definitions(${target} PRIVATE + $ + ) + + # Compile options (includes compiler flags) + target_compile_options(${target} PRIVATE + $ + $ + ) + + # liboqs depends on unistd.h, which ultimately needs the generated syscall_list.h file, + # which is generated as part of ${SYSCALL_LIST_H_TARGET} target. Therefore, we have to + # make sure that target is built before liboqs. + add_dependencies(${target} ${SYSCALL_LIST_H_TARGET}) + + # We don't want position independent code + set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE OFF) + endforeach() + + # Link the liboqs library + zephyr_link_libraries(oqs) + + # Include the liboqs headers + zephyr_include_directories(${CMAKE_CURRENT_BINARY_DIR}/build/include) + + if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7") + # Undo the workaround from above to not interfere with other modules + set(CMAKE_SYSTEM_PROCESSOR "arm") + elseif(CMAKE_SYSTEM_PROCESSOR EQUAL CMAKE_HOST_SYSTEM_PROCESSOR) + # Undo the workaround from above to not interfere with other modules + set(CMAKE_SYSTEM_PROCESSOR "posix") + endif() +endif() \ No newline at end of file diff --git a/zephyr/Kconfig b/zephyr/Kconfig new file mode 100644 index 0000000000..ef5da803d5 --- /dev/null +++ b/zephyr/Kconfig @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: MIT + +config LIBOQS + bool "Enable liboqs" + depends on ARM || ARM64 || X86 || ARCH_POSIX + help + This option enables the liboqs as a Zephyr module. Currenty, the port is only + available for ARM, ARM64, and x86 architectures and the native Posix simulators. + +menu "Liboqs algorithm configuration" + +config LIBOQS_ENABLE_KEM_BIKE + bool "Enable the BIKE KEM algorithm" + default n + depends on LIBOQS + help + This option enables the BIKE KEM algorithm. + +config LIBOQS_ENABLE_KEM_FRODOKEM + bool "Enable the FRODOKEM KEM algorithm" + default n + depends on LIBOQS + +config LIBOQS_ENABLE_KEM_NTRUPRIME + bool "Enable the NRTUPRIME KEM algorithm" + default n + depends on LIBOQS + +config LIBOQS_ENABLE_KEM_CLASSIC_MCELIECE + bool "Enable the CLASSIC_MCELIECE KEM algorithm" + default n + depends on LIBOQS + +config LIBOQS_ENABLE_KEM_HQC + bool "Enable the HQC KEM algorithm" + default n + depends on LIBOQS + +config LIBOQS_ENABLE_KEM_KYBER + bool "Enable the KYBER KEM algorithm" + default y + depends on LIBOQS + +config LIBOQS_ENABLE_SIG_DILITHIUM + bool "Enable the DILITHIUM signature algorithm" + default y + depends on LIBOQS + +config LIBOQS_ENABLE_SIG_FALCON + bool "Enable the FALCON signature algorithm" + default y + depends on LIBOQS + +config LIBOQS_ENABLE_SIG_SPHINCS + bool "Enable the SPHINCS signature algorithm" + default y + depends on LIBOQS + +endmenu diff --git a/zephyr/README.md b/zephyr/README.md new file mode 100644 index 0000000000..fd002879b5 --- /dev/null +++ b/zephyr/README.md @@ -0,0 +1,54 @@ +# Zephyr Project Port + +liboqs can be used as a module for the [Zephyr RTOS](https://www.zephyrproject.org/). + +## Installation + +You have to add liboqs to your West workspace using a [West Manifest](https://docs.zephyrproject.org/latest/develop/west/manifest.html#west-manifests) + +In your manifest file (`west.yml`), add the following: +``` +remotes: + # + - name: liboqs + url-base: https://github.com/open-quantum-safe + +projects: + # + - name: liboqs + path: modules/crypto/liboqs + revision: main + remote: liboqs +``` + +After adding the new information to your manifest file, run `west update` to download and install liboqs as a Zephyr module. After that, you can use it in your projects. + +Currently, Zephyr versions 3.4 and 3.5 are supported. Please feel free to open an issue or a PR in case you need another version supported (without guarantee that older versions can be supported at all). + +## Usage + +### Disclaimer regarding random number generation + +In order to properly use the Zephyr port of liboqs, you have to provide a custom callback function for random number generation using the `OQS_randombytes_custom_algorithm()` API. Otherwise, all key generation and signing operations will fail. + +In the two provided sample applications (also see [Samples](#Samples)), a callback is set using the default `sys_rand_get()` method from Zephyr. This method, however, does **not** provide random data suitable for cryptographic operations and is only good for testing purposes. Make sure to use a proper entroy source from your hardware to obtain actual random data. + +### Configuration + +The port provides a variety of configurable options using Kconfig. Once you have the liboqs module enabled with `CONFIG_LIBOQS=y`, you can manually enable or disable specific KEM or Signature algorithms using the `LIBOQS_ENABLE_KEM_xxx` and `LIBOQS_ENABLE_SIG_xxx` options. + +The algorithms to be standardized by NIST are enabled by default, all others are disabled by default. + +### Supported architectures + +At the moment, the following architectures are supported with the Zephyr port: +* 32-bit ARM +* 64-bit ARM +* x86 +* Native Posix + +Other architectures supported by Zephyr are not supported with this port, as liboqs itself doesn't support these architectures (the modules can't be enabled in those cases). Please feel free to raise an issue or PR in case a new architecture is required. + +### Samples + +Two sample applications are provided, demonstrating the usage of the library from within Zephyr. See [`samples/KEMs`](https://github.com/open-quantum-safe/liboqs/tree/main/zephyr/samples/Signatures) and [`samples/Signatures`](https://github.com/open-quantum-safe/liboqs/tree/main/zephyr/samples/KEMs). \ No newline at end of file diff --git a/zephyr/module.yml b/zephyr/module.yml new file mode 100644 index 0000000000..6753ab869d --- /dev/null +++ b/zephyr/module.yml @@ -0,0 +1,4 @@ +name: liboqs +build: + cmake: zephyr + kconfig: zephyr/Kconfig diff --git a/zephyr/samples/KEMs/CMakeLists.txt b/zephyr/samples/KEMs/CMakeLists.txt new file mode 100644 index 0000000000..2111496667 --- /dev/null +++ b/zephyr/samples/KEMs/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(NO_BUILD_TYPE_WARNING ON) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(liboqs_kems) + +target_sources(app PRIVATE + src/main.c +) diff --git a/zephyr/samples/KEMs/prj.conf b/zephyr/samples/KEMs/prj.conf new file mode 100644 index 0000000000..b02d6d4f24 --- /dev/null +++ b/zephyr/samples/KEMs/prj.conf @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MIT + +CONFIG_LIBOQS=y + +# Enable all KEMs that are disabled by default +CONFIG_LIBOQS_ENABLE_KEM_BIKE=y +CONFIG_LIBOQS_ENABLE_KEM_FRODOKEM=y +CONFIG_LIBOQS_ENABLE_KEM_NTRUPRIME=y +CONFIG_LIBOQS_ENABLE_KEM_CLASSIC_MCELIECE=n +CONFIG_LIBOQS_ENABLE_KEM_HQC=y + +CONFIG_PICOLIBC=y +CONFIG_TEST_RANDOM_GENERATOR=y + +# Set the stack size to 512K +CONFIG_MAIN_STACK_SIZE=524288 + +# Enable malloc and set the available size to 256K +CONFIG_COMMON_LIBC_MALLOC=y +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=262144 + +CONFIG_SPEED_OPTIMIZATIONS=y +CONFIG_LOG=y diff --git a/zephyr/samples/KEMs/sample.yaml b/zephyr/samples/KEMs/sample.yaml new file mode 100644 index 0000000000..664f86169c --- /dev/null +++ b/zephyr/samples/KEMs/sample.yaml @@ -0,0 +1,16 @@ +sample: + description: liboqs KEM example + name: liboqs_kem_example +common: + harness: console + harness_config: + type: one_line + regex: + - "Test done" + +tests: + sample.crypto.liboqs_kem_example: + timeout: 300 + integration_platforms: + - qemu_x86 + - qemu_cortex_a53 diff --git a/zephyr/samples/KEMs/src/main.c b/zephyr/samples/KEMs/src/main.c new file mode 100644 index 0000000000..a3a0e1354b --- /dev/null +++ b/zephyr/samples/KEMs/src/main.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: MIT + +#include +#include +#include + +// Autogenerated header file from Zephyr containing the version number +#include + +#if KERNEL_VERSION_NUMBER >= 0x30500 +#include +#else +#include +#endif + +#include + + +typedef struct magic_s { + uint8_t val[31]; +} magic_t; + + +void zephyr_randombytes(uint8_t *random_array, size_t bytes_to_read) +{ + // Obtain random bytes from the zephyr RNG + sys_rand_get(random_array, bytes_to_read); +} + + +/* Displays hexadecimal strings */ +static void OQS_print_hex_string(const char *label, const uint8_t *str, size_t len) { + printf("%-20s (%4zu bytes): ", label, len); + for (size_t i = 0; i < (len); i++) { + printf("%02X", str[i]); + } + printf("\n"); +} + +static OQS_STATUS kem_test_correctness(const char *method_name) +{ + OQS_KEM *kem = NULL; + uint8_t *public_key = NULL; + uint8_t *secret_key = NULL; + uint8_t *ciphertext = NULL; + uint8_t *shared_secret_e = NULL; + uint8_t *shared_secret_d = NULL; + OQS_STATUS rc, ret = OQS_ERROR; + int rv; + + //The magic numbers are random values. + //The length of the magic number was chosen to be 31 to break alignment + magic_t magic; + OQS_randombytes(magic.val, sizeof(magic_t)); + + kem = OQS_KEM_new(method_name); + if (kem == NULL) { + fprintf(stderr, "ERROR: OQS_KEM_new failed\n"); + goto err; + } + + printf("================================================================================\n"); + printf("Sample computation for KEM %s\n", kem->method_name); + printf("================================================================================\n"); + + public_key = malloc(kem->length_public_key + 2 * sizeof(magic_t)); + secret_key = malloc(kem->length_secret_key + 2 * sizeof(magic_t)); + ciphertext = malloc(kem->length_ciphertext + 2 * sizeof(magic_t)); + shared_secret_e = malloc(kem->length_shared_secret + 2 * sizeof(magic_t)); + shared_secret_d = malloc(kem->length_shared_secret + 2 * sizeof(magic_t)); + + if ((public_key == NULL) || (secret_key == NULL) || (ciphertext == NULL) || (shared_secret_e == NULL) || (shared_secret_d == NULL)) { + fprintf(stderr, "ERROR: malloc failed\n"); + goto err; + } + + //Set the magic numbers before + memcpy(public_key, magic.val, sizeof(magic_t)); + memcpy(secret_key, magic.val, sizeof(magic_t)); + memcpy(ciphertext, magic.val, sizeof(magic_t)); + memcpy(shared_secret_e, magic.val, sizeof(magic_t)); + memcpy(shared_secret_d, magic.val, sizeof(magic_t)); + + public_key += sizeof(magic_t); + secret_key += sizeof(magic_t); + ciphertext += sizeof(magic_t); + shared_secret_e += sizeof(magic_t); + shared_secret_d += sizeof(magic_t); + + // and after + memcpy(public_key + kem->length_public_key, magic.val, sizeof(magic_t)); + memcpy(secret_key + kem->length_secret_key, magic.val, sizeof(magic_t)); + memcpy(ciphertext + kem->length_ciphertext, magic.val, sizeof(magic_t)); + memcpy(shared_secret_e + kem->length_shared_secret, magic.val, sizeof(magic_t)); + memcpy(shared_secret_d + kem->length_shared_secret, magic.val, sizeof(magic_t)); + + rc = OQS_KEM_keypair(kem, public_key, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_KEM_keypair failed\n"); + goto err; + } + + rc = OQS_KEM_encaps(kem, ciphertext, shared_secret_e, public_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_KEM_encaps failed\n"); + goto err; + } + + rc = OQS_KEM_decaps(kem, shared_secret_d, ciphertext, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_KEM_decaps failed\n"); + goto err; + } + + rv = memcmp(shared_secret_e, shared_secret_d, kem->length_shared_secret); + if (rv != 0) { + fprintf(stderr, "ERROR: shared secrets are not equal\n"); + OQS_print_hex_string("shared_secret_e", shared_secret_e, kem->length_shared_secret); + OQS_print_hex_string("shared_secret_d", shared_secret_d, kem->length_shared_secret); + goto err; + } else { + printf("shared secrets are equal\n"); + } + + // test invalid encapsulation (call should either fail or result in invalid shared secret) + OQS_randombytes(ciphertext, kem->length_ciphertext); + rc = OQS_KEM_decaps(kem, shared_secret_d, ciphertext, secret_key); + if (rc == OQS_SUCCESS && memcmp(shared_secret_e, shared_secret_d, kem->length_shared_secret) == 0) { + fprintf(stderr, "ERROR: OQS_KEM_decaps succeeded on wrong input\n"); + goto err; + } + + rv = memcmp(public_key + kem->length_public_key, magic.val, sizeof(magic_t)); + rv |= memcmp(secret_key + kem->length_secret_key, magic.val, sizeof(magic_t)); + rv |= memcmp(ciphertext + kem->length_ciphertext, magic.val, sizeof(magic_t)); + rv |= memcmp(shared_secret_e + kem->length_shared_secret, magic.val, sizeof(magic_t)); + rv |= memcmp(shared_secret_d + kem->length_shared_secret, magic.val, sizeof(magic_t)); + rv |= memcmp(public_key - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(secret_key - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(ciphertext - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(shared_secret_e - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(shared_secret_d - sizeof(magic_t), magic.val, sizeof(magic_t)); + if (rv != 0) { + fprintf(stderr, "ERROR: Magic numbers do not match\n"); + goto err; + } + + ret = OQS_SUCCESS; + goto cleanup; + +err: + ret = OQS_ERROR; + +cleanup: + if (secret_key) { + OQS_MEM_secure_free(secret_key - sizeof(magic_t), kem->length_secret_key + 2 * sizeof(magic_t)); + } + if (shared_secret_e) { + OQS_MEM_secure_free(shared_secret_e - sizeof(magic_t), kem->length_shared_secret + 2 * sizeof(magic_t)); + } + if (shared_secret_d) { + OQS_MEM_secure_free(shared_secret_d - sizeof(magic_t), kem->length_shared_secret + 2 * sizeof(magic_t)); + } + if (public_key) { + OQS_MEM_insecure_free(public_key - sizeof(magic_t)); + } + if (ciphertext) { + OQS_MEM_insecure_free(ciphertext - sizeof(magic_t)); + } + OQS_KEM_free(kem); + + return ret; +} + + +int main(void) +{ + OQS_STATUS rc; + + printf("Testing KEM algorithms using liboqs version %s\n", OQS_version()); + + OQS_init(); + + /* Set a RNG callback for Zephyr */ + OQS_randombytes_custom_algorithm(zephyr_randombytes); + + if (OQS_KEM_alg_count() == 0) { + printf("No KEM algorithms enabled!\n"); + OQS_destroy(); + return EXIT_FAILURE; + } + + for (int i = 0; i < OQS_KEM_alg_count(); i++) { + const char *alg_name = OQS_KEM_alg_identifier(i); + if (!OQS_KEM_alg_is_enabled(alg_name)) { + printf("KEM algorithm %s not enabled!\n", alg_name); + } + else { + rc = kem_test_correctness(alg_name); + + if (rc != OQS_SUCCESS) { + OQS_destroy(); + return EXIT_FAILURE; + } + } + } + + OQS_destroy(); + + printf("Test done\n"); + + return EXIT_SUCCESS; +} diff --git a/zephyr/samples/Signatures/CMakeLists.txt b/zephyr/samples/Signatures/CMakeLists.txt new file mode 100644 index 0000000000..ac7f248863 --- /dev/null +++ b/zephyr/samples/Signatures/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) + +set(NO_BUILD_TYPE_WARNING ON) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(liboqs_signatures) + +target_sources(app PRIVATE + src/main.c +) diff --git a/zephyr/samples/Signatures/prj.conf b/zephyr/samples/Signatures/prj.conf new file mode 100644 index 0000000000..8c36c31edc --- /dev/null +++ b/zephyr/samples/Signatures/prj.conf @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT + +CONFIG_LIBOQS=y + +CONFIG_PICOLIBC=y +CONFIG_TEST_RANDOM_GENERATOR=y + +# Set the stack size to 128K +CONFIG_MAIN_STACK_SIZE=131072 + +# Enable malloc and set the available size to 128K +CONFIG_COMMON_LIBC_MALLOC=y +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=131072 + +CONFIG_SPEED_OPTIMIZATIONS=y +CONFIG_LOG=y diff --git a/zephyr/samples/Signatures/sample.yaml b/zephyr/samples/Signatures/sample.yaml new file mode 100644 index 0000000000..9c66001051 --- /dev/null +++ b/zephyr/samples/Signatures/sample.yaml @@ -0,0 +1,16 @@ +sample: + description: liboqs Signature example + name: lioqs_signature_example +common: + harness: console + harness_config: + type: one_line + regex: + - "Test done" + +tests: + sample.crypto.liboqs_signature_example: + timeout: 600 + integration_platforms: + - qemu_x86 + - qemu_cortex_a53 diff --git a/zephyr/samples/Signatures/src/main.c b/zephyr/samples/Signatures/src/main.c new file mode 100644 index 0000000000..6e8b596596 --- /dev/null +++ b/zephyr/samples/Signatures/src/main.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT + +#include +#include +#include + +// Autogenerated header file from Zephyr containing the version number +#include + +#if KERNEL_VERSION_NUMBER >= 0x30500 +#include +#else +#include +#endif + +#include + + +typedef struct magic_s { + uint8_t val[31]; +} magic_t; + + + +void zephyr_randombytes(uint8_t *random_array, size_t bytes_to_read) +{ + // Obtain random bytes from the zephyr RNG + sys_rand_get(random_array, bytes_to_read); +} + + +static OQS_STATUS sig_test_correctness(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 *signature = NULL; + size_t signature_len; + OQS_STATUS rc, ret = OQS_ERROR; + + //The magic numbers are random values. + //The length of the magic number was chosen to be 31 to break alignment + magic_t magic; + OQS_randombytes(magic.val, sizeof(magic_t)); + + sig = OQS_SIG_new(method_name); + if (sig == NULL) { + fprintf(stderr, "ERROR: OQS_SIG_new failed\n"); + goto err; + } + + printf("================================================================================\n"); + printf("Sample computation for signature %s\n", sig->method_name); + printf("================================================================================\n"); + + public_key = malloc(sig->length_public_key + 2 * sizeof(magic_t)); + secret_key = malloc(sig->length_secret_key + 2 * sizeof(magic_t)); + message = malloc(message_len + 2 * sizeof(magic_t)); + signature = malloc(sig->length_signature + 2 * sizeof(magic_t)); + + if ((public_key == NULL) || (secret_key == NULL) || (message == NULL) || (signature == NULL)) { + fprintf(stderr, "ERROR: malloc failed\n"); + goto err; + } + + //Set the magic numbers before + memcpy(public_key, magic.val, sizeof(magic_t)); + memcpy(secret_key, magic.val, sizeof(magic_t)); + memcpy(message, magic.val, sizeof(magic_t)); + memcpy(signature, magic.val, sizeof(magic_t)); + + public_key += sizeof(magic_t); + secret_key += sizeof(magic_t); + message += sizeof(magic_t); + signature += sizeof(magic_t); + + // and after + memcpy(public_key + sig->length_public_key, magic.val, sizeof(magic_t)); + memcpy(secret_key + sig->length_secret_key, magic.val, sizeof(magic_t)); + memcpy(message + message_len, magic.val, sizeof(magic_t)); + memcpy(signature + sig->length_signature, magic.val, sizeof(magic_t)); + + 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, signature, &signature_len, message, message_len, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_sign failed\n"); + goto err; + } + + rc = OQS_SIG_verify(sig, message, message_len, signature, signature_len, public_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_verify failed\n"); + goto err; + } + + /* modify the signature to invalidate it */ + OQS_randombytes(signature, signature_len); + rc = OQS_SIG_verify(sig, message, message_len, signature, signature_len, public_key); + if (rc != OQS_ERROR) { + fprintf(stderr, "ERROR: OQS_SIG_verify should have failed!\n"); + goto err; + } + + /* check magic values */ + int rv = memcmp(public_key + sig->length_public_key, magic.val, sizeof(magic_t)); + rv |= memcmp(secret_key + sig->length_secret_key, magic.val, sizeof(magic_t)); + rv |= memcmp(message + message_len, magic.val, sizeof(magic_t)); + rv |= memcmp(signature + sig->length_signature, magic.val, sizeof(magic_t)); + rv |= memcmp(public_key - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(secret_key - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(message - sizeof(magic_t), magic.val, sizeof(magic_t)); + rv |= memcmp(signature - sizeof(magic_t), magic.val, sizeof(magic_t)); + if (rv) { + fprintf(stderr, "ERROR: Magic numbers do not mtach\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 - sizeof(magic_t), sig->length_secret_key + 2 * sizeof(magic_t)); + } + if (public_key) { + OQS_MEM_insecure_free(public_key - sizeof(magic_t)); + } + if (message) { + OQS_MEM_insecure_free(message - sizeof(magic_t)); + } + if (signature) { + OQS_MEM_insecure_free(signature - sizeof(magic_t)); + } + OQS_SIG_free(sig); + + return ret; +} + + +int main(void) +{ + OQS_STATUS rc; + + printf("Testing signature algorithms using liboqs version %s\n", OQS_version()); + + OQS_init(); + + /* Set a RNG callback for Zephyr */ + OQS_randombytes_custom_algorithm(zephyr_randombytes); + + if (OQS_SIG_alg_count() == 0) { + printf("No signature algorithms enabled!\n"); + OQS_destroy(); + return EXIT_FAILURE; + } + + for (int i = 0; i < OQS_SIG_alg_count(); i++) { + const char *alg_name = OQS_SIG_alg_identifier(i); + if (!OQS_SIG_alg_is_enabled(alg_name)) { + printf("Signature algorithm %s not enabled!\n", alg_name); + OQS_destroy(); + return EXIT_FAILURE; + } + + rc = sig_test_correctness(alg_name); + + if (rc != OQS_SUCCESS) { + OQS_destroy(); + return EXIT_FAILURE; + } + } + + OQS_destroy(); + + printf("Test done\n"); + + return EXIT_SUCCESS; +}