├── .clang-format ├── .gitattributes ├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── CHANGES.md ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── Doxyfile ├── LICENSE.txt ├── README.md ├── RELEASE.md ├── cmake ├── examples.cmake ├── liboqs-cppConfig.cmake.in ├── liboqs-cpp_dependencies.cmake └── liboqs-cpp_uninstall.cmake.in ├── examples ├── kem.cpp ├── rand.cpp ├── sig.cpp └── standalone │ ├── CMakeLists.txt │ └── src │ └── main.cpp ├── include ├── common.hpp ├── oqs_cpp.hpp └── rand │ └── rand.hpp ├── prettyprint.sh └── unit_tests ├── CMakeLists.txt └── tests ├── main.cpp ├── test_kem.cpp └── test_sig.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | ColumnLimit: 80 4 | Standard: c++17 5 | IndentWidth: 4 6 | TabWidth: 4 7 | UseTab: Never 8 | PointerAlignment: Left 9 | IndentCaseLabels: true 10 | AlwaysBreakTemplateDeclarations: Yes 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: GitHub actions 2 | 3 | on: 4 | push: 5 | branches: ["**"] 6 | pull_request: 7 | branches: ["**"] 8 | repository_dispatch: 9 | types: ["**"] 10 | 11 | env: 12 | BUILD_TYPE: Debug 13 | LD_LIBRARY_PATH: /usr/local/lib 14 | WIN_LIBOQS_INSTALL_PATH: C:\liboqs 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, macos-latest, windows-latest] 21 | runs-on: ${{ matrix.os }} 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: Install liboqs POSIX 27 | if: matrix.os != 'windows-latest' 28 | run: | 29 | git clone --branch main --single-branch --depth 1 https://github.com/open-quantum-safe/liboqs 30 | cmake -S liboqs -B liboqs/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_SHARED_LIBS=ON -DOQS_BUILD_ONLY_LIB=ON 31 | cmake --build liboqs/build --parallel 4 32 | sudo cmake --build liboqs/build --target install 33 | 34 | - name: Configure and install liboqs-cpp POSIX 35 | if: matrix.os != 'windows-latest' 36 | run: | 37 | cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 38 | sudo cmake --build build --target install 39 | 40 | - name: Build standalone example POSIX 41 | if: matrix.os != 'windows-latest' 42 | run: | 43 | cmake -S examples/standalone -B examples/standalone/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 44 | cmake --build examples/standalone/build 45 | 46 | - name: Run standalone example POSIX 47 | if: matrix.os != 'windows-latest' 48 | run: | 49 | ./examples/standalone/build/standalone 50 | 51 | - name: Build examples POSIX 52 | if: matrix.os != 'windows-latest' 53 | run: | 54 | cmake --build build --target examples --parallel 4 55 | 56 | - name: Build unit tests POSIX 57 | if: matrix.os != 'windows-latest' 58 | run: | 59 | cmake --build build/unit_tests --target unit_tests --parallel 4 60 | 61 | - name: Run examples POSIX 62 | if: matrix.os != 'windows-latest' 63 | run: | 64 | ./build/kem 65 | echo 66 | ./build/sig 67 | echo 68 | ./build/rand 69 | 70 | - name: Run unit tests POSIX 71 | if: matrix.os != 'windows-latest' 72 | run: GTEST_COLOR=1 ctest -V --test-dir build 73 | 74 | - name: Install liboqs Windows 75 | if: matrix.os == 'windows-latest' 76 | shell: cmd 77 | run: | 78 | git clone --branch main --single-branch --depth 1 https://github.com/open-quantum-safe/liboqs 79 | cmake -S liboqs -B liboqs\build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=${{env.WIN_LIBOQS_INSTALL_PATH}} -DBUILD_SHARED_LIBS=ON -DOQS_BUILD_ONLY_LIB=ON 80 | cmake --build liboqs\build --parallel 4 81 | cmake --build liboqs\build --target install 82 | 83 | - name: Configure and install liboqs-cpp Windows 84 | if: matrix.os == 'windows-latest' 85 | shell: cmd 86 | run: | 87 | cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBOQS_INCLUDE_DIR=${{env.WIN_LIBOQS_INSTALL_PATH}}\include -DLIBOQS_LIB_DIR=${{env.WIN_LIBOQS_INSTALL_PATH}}\lib 88 | cmake --build build --target install 89 | 90 | - name: Build standalone example Windows 91 | if: matrix.os == 'windows-latest' 92 | shell: cmd 93 | run: | 94 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 95 | cmake -S examples\standalone -B examples\standalone\build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBOQS_INCLUDE_DIR=${{env.WIN_LIBOQS_INSTALL_PATH}}\include -DLIBOQS_LIB_DIR=${{env.WIN_LIBOQS_INSTALL_PATH}}\lib 96 | cmake --build examples\standalone\build --target standalone 97 | 98 | - name: Run standalone example Windows 99 | if: matrix.os == 'windows-latest' 100 | shell: cmd 101 | run: | 102 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 103 | .\examples\standalone\build\${{env.BUILD_TYPE}}\standalone.exe 104 | 105 | - name: Build examples Windows 106 | if: matrix.os == 'windows-latest' 107 | shell: cmd 108 | run: | 109 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 110 | cmake --build build --target examples --parallel 4 111 | 112 | - name: Build unit tests Windows 113 | if: matrix.os == 'windows-latest' 114 | shell: cmd 115 | run: | 116 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 117 | cmake --build build/unit_tests --target unit_tests --parallel 4 118 | 119 | - name: Run examples Windows 120 | if: matrix.os == 'windows-latest' 121 | shell: cmd 122 | run: | 123 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 124 | .\build\${{env.BUILD_TYPE}}\kem.exe 125 | echo. 126 | .\build\${{env.BUILD_TYPE}}\sig.exe 127 | echo. 128 | .\build\${{env.BUILD_TYPE}}\rand.exe 129 | 130 | - name: Run unit tests Windows 131 | shell: cmd 132 | if: matrix.os == 'windows-latest' 133 | run: | 134 | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin 135 | set GTEST_COLOR=1 136 | ctest -V --test-dir build 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Vim 35 | *.swp 36 | 37 | # Emacs 38 | \#*\# 39 | *~ 40 | 41 | # clangd 42 | .cache/ 43 | compile_commands.json 44 | 45 | # MSVC 46 | .vs/ 47 | /out* 48 | CMakeSettings.json 49 | 50 | # VS Code 51 | .vscode/ 52 | 53 | # CLion 54 | .idea/ 55 | build/ 56 | cmake-build*/ 57 | 58 | # Documentation source files 59 | doc/ 60 | 61 | # Misc 62 | .DS_Store 63 | .cmake/ 64 | Testing/ 65 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Pre-release 2 | 3 | - Added a utility function for de-structuring version strings in `` 4 | - `std::tuple 5 | oqs::version(const std::string& version)` - Returns a tuple containing the 6 | (major, minor, patch) versions 7 | - A warning is issued only if the liboqs-cpp version's major and minor numbers 8 | differ from those of liboqs, ignoring the patch version 9 | 10 | # Version 0.12.0 - January 15, 2025 11 | 12 | - Fixes https://github.com/open-quantum-safe/liboqs-cpp/issues/21. The API that 13 | NIST has introduced in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) 14 | for ML-DSA includes a context string of length >= 0. Added new API for 15 | signing with a context string 16 | - `bytes Signature::sign_with_ctx_str(const bytes& message, 17 | const bytes& context) const` 18 | - `bool Signature::verify_with_ctx_str(const bytes& message, 19 | const bytes& signature, const bytes& context, const bytes& public_key) const` 20 | - Updated examples to use `ML-KEM` and `ML-DSA` as the defaults 21 | 22 | # Version 0.10.0 - March 27, 2024 23 | 24 | - Replaced CHANGES by 25 | [CHANGES.md](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/CHANGES.md), 26 | as we now use Markdown format to keep track of changes in new releases 27 | - Removed GoogleTest dependency; if not detected, it is installed automatically 28 | as build dependency by CMake 29 | - Bumped GoogleTest version to HEAD latest, as 30 | [recommended by Google](https://github.com/google/googletest?tab=readme-ov-file#live-at-head) 31 | - Removed the NIST PRNG as the latter is no longer exposed by liboqs' public 32 | API 33 | 34 | # Version 0.9.1 - November 1, 2023 35 | 36 | - Added support for install/uninstall via CMake, and a standalone example in 37 | ["examples/standalone"]. 38 | 39 | # Version 0.9.0 - October 30, 2023 40 | 41 | - No modifications, release bumped to match the latest release of liboqs 42 | 43 | # Version 0.8.0 - July 5, 2023 44 | 45 | - This is a maintenance release, minor fixes 46 | - Minimalistic Docker support 47 | - Removed AppVeyor and CircleCI, all continuous integration is now done via 48 | GitHub actions 49 | - Changed header files extension from `.h` to `.hpp` 50 | 51 | # Version 0.7.2 - September 1, 2022 52 | 53 | - Added library version retrieval functions 54 | - `std::string oqs::oqs_version()` 55 | - `std::string oqs::oqs_cpp_version()` 56 | - Bumped GoogleTest version to 1.12.1 57 | [commit](https://github.com/google/googletest/commit/58d77fa8070e8cec2dc1ed015d66b454c8d78850) 58 | 59 | # Version 0.7.1 - January 5, 2022 60 | 61 | - Release numbering updated to match liboqs 62 | - Integrated the unit tests with the main project, so now the unit tests are 63 | automatically compiled along with the examples, and can now be run by typing 64 | `make test` or `ctest`. Use `GTEST_COLOR=1 ARGS="-V" make test` or 65 | `GTEST_COLOR=1 ctest -V` for coloured verbose testing output. 66 | - CMake minimum required version bumped to 3.10 (3.12 for macOS) for automatic 67 | unit tests detection by CMake 68 | - Switched continuous integration from Travis CI to CircleCI, we now support 69 | macOS & Linux (CircleCI) and Windows (AppVeyor) 70 | 71 | # Version 0.4.0 - November 28, 2020 72 | 73 | - Renamed `master` branch to `main` 74 | 75 | # Version 0.3.0 - June 10, 2020 76 | 77 | - Removed the Visual Studio solution (since it can be automatically generated 78 | by CMake), as we prefer to use CMake uniformly across all platforms 79 | - Minor fixes 80 | 81 | # Version 0.2.2 - January 16, 2020 82 | 83 | - Added additional RNG example project to Visual Studio solution 84 | 85 | # Version 0.2.1 - November 2, 2019 86 | 87 | - Added support for RNGs from `` 88 | - Concurrent unit testing 89 | 90 | # Version 0.2.0 - October 8, 2019 91 | 92 | - Minor changes to accommodate for liboqs API changes 93 | 94 | # Version 0.1.2 - July 9, 2019 95 | 96 | - Added MSVC support for CMake 97 | - Updated Google Test to version 1.8.1 98 | - Bugfix in `oqs::Signature::sign()` 99 | 100 | # Version 0.1.1 - May 29, 2019 101 | 102 | - Minor API change: `Signature::alg_details_::length_signature` 103 | is replaced by `Signature::alg_details_::max_length_signature` 104 | 105 | # Version 0.1.0 - April 23, 2019 106 | 107 | - Initial release 108 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | set(LIBOQS_CPP_VERSION_NUM 0.12.0) 3 | set(LIBOQS_CPP_VERSION_STR "${LIBOQS_CPP_VERSION_NUM}") 4 | project( 5 | liboqs-cpp 6 | VERSION ${LIBOQS_CPP_VERSION_NUM} 7 | LANGUAGES CXX) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | set(CMAKE_CXX_EXTENSIONS OFF) 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 12 | enable_testing() 13 | 14 | # liboqs-cpp version number 15 | add_definitions(-DLIBOQS_CPP_VERSION="${LIBOQS_CPP_VERSION_STR}") 16 | 17 | # Guard against in-source builds (snippet from Eigen's CMakeLists.txt) 18 | if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) 19 | message( 20 | FATAL_ERROR 21 | "In-source builds not allowed. Please instruct CMake to use an\ 22 | out-of-source build, e.g., 23 | cmake -B build && cmake --build build 24 | You may need to remove CMakeCache.txt.") 25 | endif() 26 | 27 | add_library(liboqs-cpp INTERFACE) 28 | target_include_directories( 29 | liboqs-cpp INTERFACE $ 30 | $) 31 | target_compile_definitions( 32 | liboqs-cpp INTERFACE -DLIBOQS_CPP_VERSION_NUM=${LIBOQS_CPP_VERSION_NUM}) 33 | target_compile_definitions( 34 | liboqs-cpp INTERFACE -DLIBOQS_CPP_VERSION_STR="${LIBOQS_CPP_VERSION_STR}") 35 | target_compile_definitions( 36 | liboqs-cpp INTERFACE -DLIBOQS_CPP_VERSION="${LIBOQS_CPP_VERSION_STR}") 37 | 38 | # Dependencies 39 | include(cmake/liboqs-cpp_dependencies.cmake) 40 | 41 | # Unit testing 42 | add_subdirectory(${CMAKE_SOURCE_DIR}/unit_tests/ EXCLUDE_FROM_ALL SYSTEM) 43 | 44 | # Enable all warnings for GNU gcc and Clang/AppleClang 45 | if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} 46 | STREQUAL "GNU") 47 | add_compile_options("-pedantic" "-Wall" "-Wextra" "-Weffc++") 48 | if(${SANITIZE}) 49 | if(NOT (${CMAKE_CXX_COMPILER_ID} MATCHES "AppleClang")) 50 | list(APPEND SANITIZE_OPTIONS -fsanitize=undefined) 51 | add_compile_options("${SANITIZE_OPTIONS}") 52 | set(CMAKE_EXE_LINKER_FLAGS 53 | "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZE_OPTIONS}") 54 | endif() 55 | endif() 56 | endif() 57 | 58 | # Examples 59 | include(cmake/examples.cmake) 60 | # END LOCAL stuff 61 | 62 | include_directories(SYSTEM "${LIBOQS_INCLUDE_DIR}") 63 | link_directories("${LIBOQS_LIB_DIR}") 64 | 65 | # Installation 66 | set(LIBOQS_CPP_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME}") 67 | install(DIRECTORY include/ DESTINATION ${LIBOQS_CPP_INSTALL_DIR}) 68 | install(TARGETS liboqs-cpp EXPORT liboqs-cpp_targets) 69 | install(EXPORT liboqs-cpp_targets DESTINATION "lib/cmake/${PROJECT_NAME}") 70 | include(CMakePackageConfigHelpers) 71 | configure_package_config_file( 72 | "cmake/liboqs-cppConfig.cmake.in" 73 | "${CMAKE_CURRENT_BINARY_DIR}/liboqs-cppConfig.cmake" 74 | INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}") 75 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liboqs-cppConfig.cmake" 76 | DESTINATION "lib/cmake/${PROJECT_NAME}") 77 | install(FILES "${CMAKE_SOURCE_DIR}/cmake/liboqs-cpp_dependencies.cmake" 78 | DESTINATION "lib/cmake/${PROJECT_NAME}") 79 | 80 | # Uninstall 81 | # https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake 82 | # 83 | # UNIX/Linux: sudo cmake --build build --target uninstall 84 | # 85 | # Windows: cmake --build build --target uninstall 86 | if(NOT TARGET uninstall) 87 | configure_file( 88 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/liboqs-cpp_uninstall.cmake.in" 89 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) 90 | if(NOT MSVC) 91 | add_custom_target( 92 | uninstall 93 | COMMAND ${CMAKE_COMMAND} -P 94 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 95 | COMMAND ${CMAKE_COMMAND} -E remove_directory 96 | "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}" 97 | COMMAND ${CMAKE_COMMAND} -E remove_directory "${LIBOQS_CPP_INSTALL_DIR}" 98 | COMMENT "Uninstall liboqs-cpp") 99 | else() 100 | add_custom_target( 101 | uninstall 102 | COMMAND ${CMAKE_COMMAND} -P 103 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 104 | COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_INSTALL_PREFIX}" 105 | COMMENT "Uninstall liboqs-cpp") 106 | endif() 107 | endif() 108 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | conduct@openquantumsafe.org. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | # Install dependencies 4 | RUN apt-get -y update && \ 5 | apt-get install -y build-essential git cmake libssl-dev 6 | 7 | # Get liboqs 8 | RUN git clone --depth 1 --branch main https://github.com/open-quantum-safe/liboqs 9 | 10 | # Install liboqs 11 | RUN cmake -S liboqs -B liboqs/build -DBUILD_SHARED_LIBS=ON && \ 12 | cmake --build liboqs/build --parallel 4 && \ 13 | cmake --build liboqs/build --target install 14 | 15 | # Enable a normal user 16 | RUN useradd -m -c "Open Quantum Safe" oqs 17 | USER oqs 18 | WORKDIR /home/oqs 19 | 20 | # Get liboqs-cpp 21 | RUN git clone --depth 1 --branch main https://github.com/open-quantum-safe/liboqs-cpp.git 22 | 23 | # Configure and install liboqs-cpp 24 | RUN cmake -S liboqs-cpp -B liboqs-cpp/build 25 | USER root 26 | RUN cmake --build liboqs-cpp/build --target install 27 | USER oqs 28 | 29 | # Build liboqs-cpp examples and unit tests 30 | RUN cmake --build liboqs-cpp/build --target examples --target unit_tests --parallel 4 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2025 Open Quantum Safe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # liboqs-cpp: C++ bindings for liboqs 2 | 3 | [![GitHub actions](https://github.com/open-quantum-safe/liboqs-cpp/actions/workflows/cmake.yml/badge.svg)](https://github.com/open-quantum-safe/liboqs-cpp/actions) 4 | 5 | --- 6 | 7 | ## About 8 | 9 | The **Open Quantum Safe (OQS) project** has the goal of developing and 10 | prototyping quantum-resistant cryptography. 11 | 12 | **liboqs-cpp** offers a C++ wrapper for 13 | the [Open Quantum Safe](https://openquantumsafe.org/) [liboqs](https://github.com/open-quantum-safe/liboqs/) 14 | C library, which is a C library for quantum-resistant cryptographic algorithms. 15 | 16 | The wrapper is written in standard C++11, hence in the following it is assumed 17 | that you have access to a C++11 compliant compiler. liboqs-cpp has been 18 | extensively tested on Linux, macOS, FreeBSD and Windows platforms. Continuous 19 | integration is provided via GitHub actions. 20 | 21 | The project contains the following files and directories: 22 | 23 | - **`include/oqs_cpp.hpp`: main header file for the wrapper** 24 | - `include/common.hpp`: utility code 25 | - `include/rand/rand.hpp`: support for RNGs from `` 26 | - `examples/kem.cpp`: key encapsulation example 27 | - `examples/rand.cpp`: RNG example 28 | - `examples/sig.cpp`: signature example 29 | - `unit_tests`: unit tests written using GoogleTest 30 | 31 | --- 32 | 33 | ## Pre-requisites 34 | 35 | - [liboqs](https://github.com/open-quantum-safe/liboqs) 36 | - [git](https://git-scm.com/) 37 | - [CMake](https://cmake.org/) 38 | - C++11 compliant compiler, e.g., [gcc](https://gcc.gnu.org/) 39 | , [clang](https://clang.llvm.org) 40 | , [MSVC](https://visualstudio.microsoft.com/vs/) etc. 41 | 42 | --- 43 | 44 | ## Installation 45 | 46 | ### Configure, build and install liboqs 47 | 48 | Execute in a Terminal/Console/Administrator Command Prompt 49 | 50 | ```shell 51 | git clone --depth=1 https://github.com/open-quantum-safe/liboqs 52 | cmake -S liboqs -B liboqs/build -DBUILD_SHARED_LIBS=ON 53 | cmake --build liboqs/build --parallel 8 54 | cmake --build liboqs/build --target install 55 | ``` 56 | 57 | The last line may require prefixing it by `sudo` on UNIX-like systems. 58 | Change `--parallel 8` to match the number of available cores on your system. 59 | 60 | On UNIX-like platforms, you may need to set 61 | the `LD_LIBRARY_PATH` (`DYLD_LIBRARY_PATH` on macOS) environment variable to 62 | point to the path to liboqs' library directory, e.g., 63 | 64 | ```shell 65 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 66 | ``` 67 | 68 | On Windows platforms, **you must ensure** that the liboqs shared 69 | library `oqs.dll` is visible system-wide, and that the following environment 70 | variable are being set. Use the "Edit the system environment variables" Control 71 | Panel tool or execute in a Command Prompt, e.g., 72 | 73 | ```shell 74 | set PATH=%PATH%;C:\Program Files (x86)\liboqs\bin 75 | ``` 76 | 77 | You can change liboqs' installation directory by configuring the build to use an 78 | alternative path, e.g., `C:\liboqs`, by replacing the first CMake line above by 79 | 80 | ```shell 81 | cmake -S liboqs -B liboqs/build -DCMAKE_INSTALL_PREFIX="C:\liboqs" -DBUILD_SHARED_LIBS=ON 82 | ``` 83 | 84 | ### Configure and install the wrapper 85 | 86 | Execute in a Terminal/Console/Administrator Command Prompt 87 | 88 | ```shell 89 | git clone --depth=1 https://github.com/open-quantum-safe/liboqs-cpp 90 | cmake -S liboqs-cpp -B liboqs-cpp/build 91 | cmake --build liboqs-cpp/build --target install 92 | ``` 93 | 94 | ### Build the examples 95 | 96 | Execute, on UNIX-like platforms 97 | 98 | ```shell 99 | cmake --build liboqs-cpp/build --target examples --parallel 8 100 | ``` 101 | 102 | and, on Windows platforms 103 | 104 | ```shell 105 | cmake --build liboqs-cpp/build --target examples -DLIBOQS_INCLUDE_DIR="C:\Program Files (x86)\liboqs\include" -DLIBOQS_LIB_DIR="C:\Program Files (x86)\liboqs\lib" --parallel 8 106 | ``` 107 | 108 | Note that you may need to change the flags `-DLIBOQS_INCLUDE_DIR` 109 | and `-DLIBOQS_LIB_DIR` to point to the correct location of `liboqs` in case you 110 | installed it in a non-standard location. 111 | 112 | To build only a specific target, e.g. `examples/kem`, specify the target as the 113 | argument of the `cmake` command, e.g., 114 | 115 | ```shell 116 | cmake --build liboqs-cpp/build --target kem 117 | ``` 118 | 119 | ### Run the examples 120 | 121 | Execute 122 | 123 | ```shell 124 | liboqs-cpp/build/kem 125 | liboqs-cpp/build/sig 126 | liboqs-cpp/build/rand 127 | ``` 128 | 129 | Note that on Windows platforms, the location and the names of the built examples 130 | may be slightly different, e.g., `liboqs-cpp/build/Debug/kem.exe`. 131 | 132 | ### Build and run the unit tests 133 | 134 | Execute 135 | 136 | ```shell 137 | cmake --build liboqs-cpp/build/unit_tests --target unit_tests --parallel 8 138 | ``` 139 | 140 | followed by 141 | 142 | ```shell 143 | ctest --test-dir liboqs-cpp/build 144 | ``` 145 | 146 | --- 147 | 148 | ## Installing liboqs-cpp and using it in standalone applications 149 | 150 | liboqs-cpp is a header-only wrapper. To use liboqs-cpp, you only need 151 | to 152 | 153 | ```cpp 154 | #include 155 | ``` 156 | 157 | in your application, and have liboqs library installed as described above. 158 | See [examples/standalone](https://github.com/open-quantum-safe/liboqs-cpp/tree/main/examples/standalone) 159 | for a standalone example. 160 | 161 | To avoid namespace pollution, liboqs-cpp includes all of its code inside the 162 | namespace `oqs`. All the liboqs C API is located in the namespace `oqs::C`, 163 | hence to use directly a C API function you must qualify the call 164 | with `oqs::C::liboqs_C_function(...)`. 165 | 166 | liboqs-cpp defines four main classes: `oqs::KeyEncapsulation` 167 | and `oqs::Signature`, providing post-quantum key encapsulation and signture 168 | mechanisms, respectively, and 169 | `oqs::KEMs` and `oqs::Sigs`, containing only static member functions that 170 | provide information related to the available key encapsulation mechanisms or 171 | signature mechanism, respectively. 172 | 173 | `oqs::KeyEncapsulation` and/or `oqs::Signature` must be instantiated with a 174 | string identifying one of mechanisms supported by liboqs; these can be 175 | enumerated using the `oqs::KEMs::get_enabled_KEM_mechanisms()` 176 | and `oqs::Sigs::get_enabled_sig_mechanisms()` member functions. 177 | 178 | Support for alternative RNGs is provided by the `include/rand/rand.hpp` header 179 | file, which exports its functions in `namespace oqs::rand`. This header file 180 | must be explicitly included in order to activate the support for alternative 181 | RNGs. 182 | 183 | The wrapper also defines a high resolution timing class, `oqs::Timer<>`. 184 | 185 | The examples in 186 | the [`examples`](https://github.com/open-quantum-safe/liboqs-cpp/tree/main/examples) 187 | directory are self-explanatory stand-alone applications and provide more details 188 | about the wrapper's API and its usage. 189 | 190 | --- 191 | 192 | ## Documentation 193 | 194 | To generate the full official API documentation in both PDF and HTML formats run 195 | [`doxygen`](http://www.doxygen.nl) on 196 | the [`Doxyfile`](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/Doxyfile) 197 | file. The tool `dot` from the [`Graphviz`](https://www.graphviz.org) package 198 | must be installed (`sudo apt-get install graphviz` in Ubuntu/Debian). 199 | Running `doxygen` will generate the documentation directory `doc` containing 200 | both the HTML and LaTeX documentation. 201 | 202 | The HTML documentation file will be accessible by opening `doc/html/index.html` 203 | with the browser of your choice. 204 | 205 | To generate a PDF file of the documentation, run 206 | 207 | ```shell 208 | latexmk -pdf refman.tex 209 | ``` 210 | 211 | from the `doc/latex` directory or compile the file `doc/latex/refman.tex` with 212 | your LaTeX compiler. This will create the `doc/latex/refman.pdf` documentation 213 | file. Consult your favourite LaTeX manual for how to compile/build LaTeX files 214 | under your specific operating system. 215 | 216 | --- 217 | 218 | ## Docker 219 | 220 | A self-explanatory minimalistic Docker file is provided 221 | in [`Dockerfile`](https://github.com/open-quantum-safe/liboqs-cpp/tree/main/Dockerfile). 222 | 223 | Build the image by executing 224 | 225 | ```shell 226 | docker build -t oqs-cpp . 227 | ``` 228 | 229 | Run, e.g., the key encapsulation example by executing 230 | 231 | ```shell 232 | docker run -it oqs-cpp sh -c "liboqs-cpp/build/kem" 233 | ``` 234 | 235 | Or, run the unit tests with 236 | 237 | ```shell 238 | docker run -it oqs-cpp sh -c "ctest --test-dir liboqs-cpp/build" 239 | ``` 240 | 241 | In case you want to use the Docker container as a development environment, mount 242 | your current project in the Docker 243 | container with 244 | 245 | ```shell 246 | docker run --rm -it --workdir=/app -v ${PWD}:/app oqs-cpp /bin/bash 247 | ``` 248 | 249 | --- 250 | 251 | ## Limitations and security 252 | 253 | liboqs is designed for prototyping and evaluating quantum-resistant 254 | cryptography. Security of proposed quantum-resistant algorithms may rapidly 255 | change as research advances, and may ultimately be completely insecure against 256 | either classical or quantum computers. 257 | 258 | We believe that the NIST Post-Quantum Cryptography standardization project is 259 | currently the best avenue to identifying potentially quantum-resistant 260 | algorithms. liboqs does not intend to "pick winners", and we strongly recommend 261 | that applications and protocols rely on the outcomes of the NIST standardization 262 | project when deploying post-quantum cryptography. 263 | 264 | We acknowledge that some parties may want to begin deploying post-quantum 265 | cryptography prior to the conclusion of the NIST standardization project. We 266 | strongly recommend that any attempts to do make use of so-called **hybrid 267 | cryptography**, in which post-quantum public-key algorithms are used alongside 268 | traditional public key algorithms (like RSA or elliptic curves) so that the 269 | solution is at least no less secure than existing traditional cryptography. 270 | 271 | Just like liboqs, liboqs-cpp is provided "as is", without warranty of any kind. 272 | See [LICENSE](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/LICENSE) 273 | for the full disclaimer. 274 | 275 | --- 276 | 277 | ## License 278 | 279 | liboqs-cpp is licensed under the MIT License; 280 | see [LICENSE](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/LICENSE) 281 | for details. 282 | 283 | --- 284 | 285 | ## Team 286 | 287 | The Open Quantum Safe project is led 288 | by [Douglas Stebila](https://www.douglas.stebila.ca/research/) 289 | and [Michele Mosca](https://faculty.iqc.uwaterloo.ca/mmosca/) at the University 290 | of Waterloo. 291 | 292 | liboqs-cpp was developed by [Vlad Gheorghiu](https://vsoftco.github.io) at 293 | [softwareQ Inc.](https://www.softwareq.ca) and at the University of Waterloo. 294 | 295 | --- 296 | 297 | ## Support 298 | 299 | Financial support for the development of Open Quantum Safe has been provided by 300 | Amazon Web Services and the Canadian Centre for Cyber Security. 301 | 302 | We'd like to make a special acknowledgement to the companies who have dedicated 303 | programmer time to contribute source code to OQS, including Amazon Web Services, 304 | evolutionQ, softwareQ, and Microsoft Research. 305 | 306 | Research projects which developed specific components of OQS have been supported 307 | by various research grants, including funding from the Natural Sciences and 308 | Engineering Research Council of Canada (NSERC); see the source papers for 309 | funding acknowledgments. 310 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # liboqs-cpp version 0.12.0 2 | 3 | --- 4 | 5 | ## About 6 | 7 | The **Open Quantum Safe (OQS) project** has the goal of developing and 8 | prototyping quantum-resistant cryptography. More information on OQS can be 9 | found on our website https://openquantumsafe.org/ and on GitHub at 10 | https://github.com/open-quantum-safe/. 11 | 12 | **liboqs** is an open source C library for quantum-resistant cryptographic 13 | algorithms. See more about liboqs at 14 | [https://github.com/open-quantum-safe/liboqs/](https://github.com/open-quantum-safe/liboqs/), 15 | including a list of supported algorithms. 16 | 17 | **liboqs-cpp** is an open source C++ wrapper for the liboqs C library for 18 | quantum-resistant cryptographic algorithms. Details about liboqs-cpp can be 19 | found in 20 | [README.md](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/README.md). 21 | See in particular limitations on intended use. 22 | 23 | --- 24 | 25 | ## Release notes 26 | 27 | This release of liboqs-cpp was released on January 15, 2025. Its release page 28 | on GitHub is 29 | https://github.com/open-quantum-safe/liboqs-cpp/releases/tag/0.12.0. 30 | 31 | --- 32 | 33 | ## What's New 34 | 35 | This is the 15th release of liboqs-cpp. For a list of changes see 36 | [CHANGES.md](https://github.com/open-quantum-safe/liboqs-cpp/blob/main/CHANGES.md). 37 | -------------------------------------------------------------------------------- /cmake/examples.cmake: -------------------------------------------------------------------------------- 1 | # Examples Source file(s) to be compiled, modify as needed 2 | aux_source_directory(${CMAKE_SOURCE_DIR}/examples EXAMPLE_FILES) 3 | 4 | # Build all examples in ${EXAMPLE_FILES} 5 | add_custom_target(examples COMMENT "Examples") 6 | foreach(file ${EXAMPLE_FILES}) 7 | get_filename_component(TARGET_NAME ${file} NAME_WE) 8 | add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL ${file}) 9 | add_dependencies(examples ${TARGET_NAME}) 10 | target_link_libraries(${TARGET_NAME} PUBLIC liboqs-cpp oqs) 11 | endforeach() 12 | -------------------------------------------------------------------------------- /cmake/liboqs-cppConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(LIBOQS_CPP_INSTALL_DIR "@LIBOQS_CPP_INSTALL_DIR@") 4 | include("${CMAKE_CURRENT_LIST_DIR}/liboqs-cpp_targets.cmake") 5 | include("${CMAKE_CURRENT_LIST_DIR}/liboqs-cpp_dependencies.cmake") 6 | message(STATUS "Found liboqs-cpp in @LIBOQS_CPP_INSTALL_DIR@") 7 | -------------------------------------------------------------------------------- /cmake/liboqs-cpp_dependencies.cmake: -------------------------------------------------------------------------------- 1 | # liboqs-cpp additional dependencies Do not modify unless you know what you're 2 | # doing 3 | 4 | # Liboqs Path to liboqs include and lib, modify as needed 5 | if(NOT WIN32) 6 | set(LIBOQS_INCLUDE_DIR 7 | "/usr/local/include" 8 | CACHE PATH "Path to liboqs include directory") 9 | set(LIBOQS_LIB_DIR 10 | "/usr/local/lib" 11 | CACHE PATH "Path to liboqs lib directory") 12 | else() 13 | # Increase the stack size to 8MB on Windows 14 | if(MSVC) 15 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") 16 | elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} 17 | STREQUAL "GNU") 18 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608") 19 | endif() 20 | set(LIBOQS_INCLUDE_DIR 21 | "" 22 | CACHE PATH "Path to liboqs include directory") 23 | set(LIBOQS_LIB_DIR 24 | "" 25 | CACHE PATH "Path to liboqs lib directory") 26 | endif() 27 | 28 | if(LIBOQS_INCLUDE_DIR STREQUAL "") 29 | message( 30 | FATAL_ERROR 31 | "Please specify the path to the liboqs include directory\ 32 | by setting the LIBOQS_INCLUDE_DIR CMake flag, i.e. 33 | cmake -DLIBOQS_INCLUDE_DIR=/path/to/liboqs/include") 34 | elseif(NOT IS_DIRECTORY ${LIBOQS_INCLUDE_DIR}) 35 | message(FATAL_ERROR "Invalid path to the liboqs include directory") 36 | endif() 37 | 38 | if(LIBOQS_LIB_DIR STREQUAL "") 39 | message( 40 | FATAL_ERROR 41 | "Please specify the path to the liboqs lib directory\ 42 | by setting the LIBOQS_LIB_DIR CMake flag, i.e. 43 | cmake -DLIBOQS_LIB_DIR=/path/to/liboqs/lib") 44 | elseif(NOT IS_DIRECTORY ${LIBOQS_LIB_DIR}) 45 | message(FATAL_ERROR "Invalid path to the liboqs lib directory") 46 | endif() 47 | 48 | include_directories(SYSTEM "${LIBOQS_INCLUDE_DIR}") 49 | link_directories("${LIBOQS_LIB_DIR}") 50 | 51 | # Default build type 52 | if(NOT CMAKE_BUILD_TYPE) 53 | set(CMAKE_BUILD_TYPE 54 | Release 55 | CACHE STRING "Choose the type of build, options are: \ 56 | None Debug Release MinSizeRel RelWithDebInfo." FORCE) 57 | endif() 58 | -------------------------------------------------------------------------------- /cmake/liboqs-cpp_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 2 | message( 3 | FATAL_ERROR 4 | "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 5 | endif() 6 | 7 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 8 | string(REGEX REPLACE "\n" ";" files "${files}") 9 | foreach(file ${files}) 10 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 11 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 12 | exec_program( 13 | "@CMAKE_COMMAND@" ARGS 14 | "-E remove \"$ENV{DESTDIR}${file}\"" 15 | OUTPUT_VARIABLE rm_out 16 | RETURN_VALUE rm_retval) 17 | if(NOT "${rm_retval}" STREQUAL 0) 18 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 19 | endif() 20 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 22 | endif() 23 | endforeach() 24 | 25 | if(NOT "@MSVC@") 26 | message(STATUS "Removing @CMAKE_INSTALL_PREFIX@/lib/cmake/@PROJECT_NAME@") 27 | message(STATUS "Removing @LIBOQS_CPP_INSTALL_DIR@") 28 | else() 29 | message(STATUS "Removing @CMAKE_INSTALL_PREFIX@") 30 | endif() 31 | -------------------------------------------------------------------------------- /examples/kem.cpp: -------------------------------------------------------------------------------- 1 | // key encapsulation C++ example 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // liboqs C++ wrapper 9 | #include "oqs_cpp.hpp" 10 | 11 | int main() { 12 | std::cout << std::boolalpha; 13 | std::cout << "liboqs version: " << oqs::oqs_version() << '\n'; 14 | std::cout << "liboqs-cpp version: " << oqs::oqs_cpp_version() << '\n'; 15 | std::cout << "Enabled KEMs:\n" << oqs::KEMs::get_enabled_KEMs(); 16 | 17 | std::string kem_name = "ML-KEM-512"; 18 | oqs::KeyEncapsulation client{kem_name}; 19 | std::cout << "\n\nKEM details:\n" << client.get_details(); 20 | 21 | oqs::Timer t; 22 | oqs::bytes client_public_key = client.generate_keypair(); 23 | t.toc(); 24 | std::cout << "\n\nClient public key:\n" << oqs::hex_chop(client_public_key); 25 | std::cout << "\n\nIt took " << t << " millisecs to generate the key pair"; 26 | 27 | oqs::KeyEncapsulation server{kem_name}; 28 | oqs::bytes ciphertext, shared_secret_server; 29 | t.tic(); 30 | std::tie(ciphertext, shared_secret_server) = 31 | server.encap_secret(client_public_key); 32 | t.toc(); 33 | std::cout << "\nIt took " << t << " millisecs to encapsulate the secret"; 34 | 35 | t.tic(); 36 | oqs::bytes shared_secret_client = client.decap_secret(ciphertext); 37 | t.toc(); 38 | std::cout << "\nIt took " << t << " millisecs to decapsulate the secret"; 39 | 40 | std::cout << "\n\nClient shared secret:\n" 41 | << oqs::hex_chop(shared_secret_client); 42 | std::cout << "\n\nServer shared secret:\n" 43 | << oqs::hex_chop(shared_secret_server); 44 | bool is_valid = (shared_secret_client == shared_secret_server); 45 | std::cout << "\n\nShared secrets coincide? " << is_valid << '\n'; 46 | 47 | return is_valid ? oqs::OQS_STATUS::OQS_SUCCESS : oqs::OQS_STATUS::OQS_ERROR; 48 | } 49 | -------------------------------------------------------------------------------- /examples/rand.cpp: -------------------------------------------------------------------------------- 1 | // various RNGs C++ example 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // RNG support 8 | #include "rand/rand.hpp" 9 | 10 | // CustomRNG provides a (trivial) custom random number generator; the memory is 11 | // provided by the caller, i.e. oqs::rand::randombytes() 12 | void custom_RNG(uint8_t* random_array, std::size_t bytes_to_read) { 13 | for (std::size_t i = 0; i < bytes_to_read; ++i) 14 | random_array[i] = static_cast(i % 256); 15 | } 16 | 17 | int main() { 18 | std::cout << "liboqs version: " << oqs::oqs_version() << '\n'; 19 | std::cout << "liboqs-cpp version: " << oqs::oqs_cpp_version() << '\n'; 20 | 21 | oqs::rand::randombytes_switch_algorithm(OQS_RAND_alg_system); 22 | std::cout << std::setw(18) << std::left; 23 | std::cout << "System (default): " << oqs::rand::randombytes(32) << '\n'; 24 | 25 | oqs::rand::randombytes_custom_algorithm(custom_RNG); 26 | std::cout << std::setw(18) << std::left; 27 | std::cout << "Custom RNG: " << oqs::rand::randombytes(32) << '\n'; 28 | 29 | // We do not yet support OpenSSL on Windows 30 | #ifndef _WIN32 31 | oqs::rand::randombytes_switch_algorithm(OQS_RAND_alg_openssl); 32 | std::cout << std::setw(18) << std::left; 33 | std::cout << "OpenSSL: " << oqs::rand::randombytes(32) << '\n'; 34 | #endif 35 | } 36 | -------------------------------------------------------------------------------- /examples/sig.cpp: -------------------------------------------------------------------------------- 1 | // signature C++ example 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // liboqs C++ wrapper 8 | #include "oqs_cpp.hpp" 9 | 10 | int main() { 11 | std::cout << std::boolalpha; 12 | std::cout << "liboqs version: " << oqs::oqs_version() << '\n'; 13 | std::cout << "liboqs-cpp version: " << oqs::oqs_cpp_version() << '\n'; 14 | std::cout << "Enabled signatures:\n" << oqs::Sigs::get_enabled_sigs(); 15 | 16 | oqs::bytes message = "This is the message to sign"_bytes; 17 | std::string sig_name = "ML-DSA-44"; 18 | oqs::Signature signer{sig_name}; 19 | std::cout << "\n\nSignature details:\n" << signer.get_details(); 20 | 21 | oqs::Timer t; 22 | oqs::bytes signer_public_key = signer.generate_keypair(); 23 | t.toc(); 24 | std::cout << "\n\nSigner public key:\n" << oqs::hex_chop(signer_public_key); 25 | std::cout << "\n\nIt took " << t << " microsecs to generate the key pair"; 26 | 27 | t.tic(); 28 | oqs::bytes signature = signer.sign(message); 29 | t.toc(); 30 | std::cout << "\nIt took " << t << " microsecs to sign the message"; 31 | std::cout << "\n\nSignature:\n" << oqs::hex_chop(signature); 32 | 33 | oqs::Signature verifier{sig_name}; 34 | bool is_valid = verifier.verify(message, signature, signer_public_key); 35 | std::cout << "\n\nValid signature? " << is_valid << '\n'; 36 | 37 | return is_valid ? oqs::OQS_STATUS::OQS_SUCCESS : oqs::OQS_STATUS::OQS_ERROR; 38 | } 39 | -------------------------------------------------------------------------------- /examples/standalone/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(standalone) 3 | set(CMAKE_CXX_STANDARD 11) 4 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 5 | 6 | # If the liboqs-cpp installation path was non-standard, i.e., specified by 7 | # 8 | # cmake -B build -DCMAKE_INSTALL_PREFIX=/path/to/installed/liboqs-cpp 9 | # 10 | # then uncomment the following line and replace the installation path with yours 11 | 12 | # set(CMAKE_PREFIX_PATH "/path/to/installed/liboqs-cpp") 13 | 14 | find_package(liboqs-cpp REQUIRED) 15 | 16 | add_executable(standalone src/main.cpp) 17 | target_link_libraries(standalone PUBLIC liboqs-cpp oqs) 18 | -------------------------------------------------------------------------------- /examples/standalone/src/main.cpp: -------------------------------------------------------------------------------- 1 | // key encapsulation C++ example 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // liboqs C++ wrapper 9 | #include 10 | 11 | int main() { 12 | std::cout << std::boolalpha; 13 | std::cout << "liboqs version: " << oqs::oqs_version() << '\n'; 14 | std::cout << "liboqs-cpp version: " << oqs::oqs_cpp_version() << '\n'; 15 | std::cout << "Enabled KEMs:\n" << oqs::KEMs::get_enabled_KEMs(); 16 | 17 | std::string kem_name = "ML-KEM-512"; 18 | oqs::KeyEncapsulation client{kem_name}; 19 | std::cout << "\n\nKEM details:\n" << client.get_details(); 20 | 21 | oqs::Timer t; 22 | oqs::bytes client_public_key = client.generate_keypair(); 23 | t.toc(); 24 | std::cout << "\n\nClient public key:\n" << oqs::hex_chop(client_public_key); 25 | std::cout << "\n\nIt took " << t << " millisecs to generate the key pair"; 26 | 27 | oqs::KeyEncapsulation server{kem_name}; 28 | oqs::bytes ciphertext, shared_secret_server; 29 | t.tic(); 30 | std::tie(ciphertext, shared_secret_server) = 31 | server.encap_secret(client_public_key); 32 | t.toc(); 33 | std::cout << "\nIt took " << t << " millisecs to encapsulate the secret"; 34 | 35 | t.tic(); 36 | oqs::bytes shared_secret_client = client.decap_secret(ciphertext); 37 | t.toc(); 38 | std::cout << "\nIt took " << t << " millisecs to decapsulate the secret"; 39 | 40 | std::cout << "\n\nClient shared secret:\n" 41 | << oqs::hex_chop(shared_secret_client); 42 | std::cout << "\n\nServer shared secret:\n" 43 | << oqs::hex_chop(shared_secret_server); 44 | bool is_valid = (shared_secret_client == shared_secret_server); 45 | std::cout << "\n\nShared secrets coincide? " << is_valid << '\n'; 46 | 47 | return is_valid ? oqs::OQS_STATUS::OQS_SUCCESS : oqs::OQS_STATUS::OQS_ERROR; 48 | } 49 | -------------------------------------------------------------------------------- /include/common.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file common.hpp 3 | * \brief Type definitions and utility functions 4 | */ 5 | 6 | #ifndef COMMON_HPP_ 7 | #define COMMON_HPP_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace oqs { 21 | namespace C { 22 | // Everything in liboqs has C linkage 23 | extern "C" { 24 | #include 25 | } 26 | } // namespace C 27 | using byte = std::uint8_t; ///< byte (unsigned) 28 | using bytes = std::vector; ///< vector of bytes (unsigned) 29 | using OQS_STATUS = C::OQS_STATUS; ///< bring OQS_STATUS into the oqs namespace 30 | 31 | /** 32 | * \brief liboqs version string 33 | * \return liboqs version string 34 | */ 35 | inline std::string oqs_version() { return oqs::C::OQS_version(); } 36 | 37 | /** 38 | * \brief liboqs-cpp version string 39 | * \return liboqs-cpp version string 40 | */ 41 | inline std::string oqs_cpp_version() { return LIBOQS_CPP_VERSION; } 42 | 43 | /** 44 | * \brief De-structure version string as a tuple (major, minor, patch) 45 | * \return Version string as a tuple (major, minor, patch) 46 | */ 47 | inline std::tuple 48 | version(const std::string& version_str) { 49 | std::stringstream ss(version_str); 50 | std::string major, minor, patch; 51 | 52 | std::getline(ss, major, '.'); 53 | std::getline(ss, minor, '.'); 54 | std::getline(ss, patch, '.'); 55 | 56 | return std::make_tuple(major, minor, patch); 57 | } 58 | 59 | /** 60 | * \brief Sets to zero the content of \a v by invoking the liboqs 61 | * OQS_MEM_cleanse() function. Use it to clean "hot" memory areas, such as 62 | * secret keys etc. 63 | * \param v Vector of bytes 64 | */ 65 | inline void mem_cleanse(bytes& v) { C::OQS_MEM_cleanse(v.data(), v.size()); } 66 | 67 | /** 68 | * \namespace internal 69 | * \brief Internal implementation details 70 | */ 71 | namespace internal { 72 | /* code from 73 | https://github.com/vsoftco/qpp/blob/master/include/internal/classes/singleton.h 74 | */ 75 | /** 76 | * \class oqs::internal::Singleton 77 | * \brief Singleton class using CRTP pattern 78 | * \note Code from 79 | * https://github.com/softwareqinc/qpp/blob/main/include/internal/classes/singleton.hpp 80 | * \tparam T Class type of which instance will become a Singleton 81 | */ 82 | template 83 | class Singleton { 84 | protected: 85 | Singleton() noexcept = default; 86 | 87 | Singleton(const Singleton&) = delete; 88 | 89 | Singleton& operator=(const Singleton&) = delete; 90 | 91 | virtual ~Singleton() = default; 92 | 93 | public: 94 | /** 95 | * \brief Singleton instance (thread-safe) via CRTP pattern 96 | * \return Singleton instance 97 | */ 98 | static T& get_instance() noexcept(std::is_nothrow_constructible::value) { 99 | // Guaranteed to be destroyed. 100 | // Instantiated on first use. 101 | // Thread safe in C++11 102 | static T instance; 103 | 104 | return instance; 105 | } 106 | }; // class Singleton 107 | 108 | /** 109 | * \class oqs::internal::HexChop 110 | * \brief std::ostream manipulator for long vectors of oqs::byte, use it to 111 | * display only a small number of elements from the beginning and end of the 112 | * vector 113 | */ 114 | class HexChop { 115 | bytes v_; ///< vector of byes 116 | std::size_t from_start_, 117 | from_end_; ///< number of hex bytes taken from the start and from the 118 | ///< end 119 | /** 120 | * \brief std::ostream manipulator 121 | * \param os Output stream 122 | * \param start Number of hex characters displayed from the beginning of 123 | * the vector 124 | * \param end Number of hex characters displayed from the end of the vector 125 | * \param is_short Vector is too short, display all hex characters 126 | */ 127 | void manipulate_ostream_(std::ostream& os, std::size_t start, 128 | std::size_t end, bool is_short) const { 129 | std::stringstream ss; 130 | ss << std::setfill('0') << std::hex << std::uppercase; 131 | 132 | bool first = true; 133 | for (std::size_t i = 0; i < start; ++i) { 134 | if (first) { 135 | first = false; 136 | ss << std::setw(2) << static_cast(v_[i]); 137 | } else { 138 | ss << " " << std::setw(2) << static_cast(v_[i]); 139 | } 140 | } 141 | 142 | if (!is_short) 143 | ss << " ... "; 144 | 145 | first = true; 146 | std::size_t v_size = v_.size(); 147 | for (std::size_t i = v_size - end; i < v_size; ++i) { 148 | if (first) { 149 | first = false; 150 | ss << static_cast(v_[i]); 151 | } else 152 | ss << " " << std::setw(2) << static_cast(v_[i]); 153 | } 154 | 155 | os << ss.str(); 156 | } 157 | 158 | public: 159 | /** 160 | * \brief Constructs an instance of oqs::internal::HexChop 161 | * \param v Vector of bytes 162 | * \param from_start Number of hex characters displayed from the beginning 163 | * of the vector 164 | * \param from_end Number of hex characters displayed from 165 | * the from_end of the vector 166 | */ 167 | explicit HexChop(oqs::bytes v, std::size_t from_start, std::size_t from_end) 168 | : v_{std::move(v)}, from_start_{from_start}, from_end_{from_end} {} 169 | 170 | /** 171 | * \brief std::ostream extraction operator for oqs::internal::HexChop 172 | * \param os Output stream 173 | * \param rhs oqs::internal::HexChop instance 174 | * \return Reference to the output stream 175 | */ 176 | friend std::ostream& operator<<(std::ostream& os, const HexChop& rhs) { 177 | 178 | bool is_short = rhs.from_start_ + rhs.from_end_ >= rhs.v_.size(); 179 | if (is_short) 180 | rhs.manipulate_ostream_(os, rhs.v_.size(), 0, true); 181 | else 182 | rhs.manipulate_ostream_(os, rhs.from_start_, rhs.from_end_, false); 183 | 184 | return os; 185 | } 186 | }; // class HexChop 187 | } // namespace internal 188 | 189 | /* code from 190 | https://github.com/vsoftco/qpp/blob/master/include/classes/timer.h 191 | */ 192 | /** 193 | * \class oqs::Timer 194 | * \brief High resolution timer 195 | * \note Code from 196 | * https://github.com/vsoftco/qpp/blob/master/include/classes/timer.h 197 | * \tparam T Tics duration, default is std::chrono::duration, 198 | * i.e. seconds in double precision 199 | * \tparam CLOCK_T Clock's type, default is std::chrono::steady_clock, 200 | * not affected by wall clock changes during runtime 201 | */ 202 | template , 203 | typename CLOCK_T = std::chrono::steady_clock> 204 | class Timer { 205 | protected: 206 | typename CLOCK_T::time_point start_, end_; 207 | 208 | public: 209 | /** 210 | * \brief Constructs an instance with the current time as the start point 211 | */ 212 | Timer() noexcept : start_{CLOCK_T::now()}, end_{start_} {} 213 | 214 | /** 215 | * \brief Resets the chronometer 216 | * 217 | * Resets the start/end point to the current time 218 | * 219 | * \return Reference to the current instance 220 | */ 221 | Timer& tic() noexcept { 222 | start_ = end_ = CLOCK_T::now(); 223 | 224 | return *this; 225 | } 226 | 227 | /** 228 | * \brief Stops the chronometer 229 | * 230 | * Set the current time as the end point 231 | * 232 | * \return Reference to the current instance 233 | */ 234 | Timer& toc() & noexcept { 235 | end_ = CLOCK_T::now(); 236 | 237 | return *this; 238 | } 239 | 240 | /** 241 | * \brief Time passed in the duration specified by T 242 | * 243 | * \return Number of tics (specified by T) that passed between the 244 | * instantiation/reset and invocation of oqs::Timer::toc() 245 | */ 246 | double tics() const noexcept { 247 | return static_cast( 248 | std::chrono::duration_cast(end_ - start_).count()); 249 | } 250 | 251 | /** 252 | * \brief Duration specified by U 253 | * 254 | * \tparam U Duration, default is T, which defaults to 255 | * std::chrono::duration, i.e. seconds in double precision 256 | * 257 | * \return Duration that passed between the 258 | * instantiation/reset and invocation of oqs::Timer::toc() 259 | */ 260 | template 261 | U get_duration() const noexcept { 262 | return std::chrono::duration_cast(end_ - start_); 263 | } 264 | 265 | /** 266 | * \brief Default virtual destructor 267 | */ 268 | virtual ~Timer() = default; 269 | 270 | friend std::ostream& operator<<(std::ostream& os, const Timer& rhs) { 271 | return os << rhs.tics(); 272 | } 273 | }; // class Timer 274 | 275 | /** 276 | * \brief Constructs an instance of oqs::internal::HexChop 277 | * \param v Vector of bytes 278 | * \param from_start Number of hex characters displayed from the beginning of 279 | * the vector 280 | * \param from_end Number of hex characters displayed from the from_end of the 281 | * vector 282 | * \return Instance of oqs::internal::HexChop 283 | */ 284 | inline internal::HexChop hex_chop(const bytes& v, std::size_t from_start = 8, 285 | std::size_t from_end = 8) { 286 | return internal::HexChop{v, from_start, from_end}; 287 | } 288 | } // namespace oqs 289 | 290 | /** 291 | * \brief std::ostream extraction operator for oqs::bytes 292 | * \param os Output stream 293 | * \param rhs Vector of oqs::byte 294 | * \return Reference to the output stream 295 | */ 296 | inline std::ostream& operator<<(std::ostream& os, const oqs::bytes& rhs) { 297 | return os << oqs::hex_chop(rhs, rhs.size(), 0); 298 | } 299 | 300 | /** 301 | * \brief std::ostream extraction operator for vectors of strings 302 | * \param os Output stream 303 | * \param rhs Vector of std::string 304 | * \return Reference to the output stream 305 | */ 306 | inline std::ostream& operator<<(std::ostream& os, 307 | const std::vector& rhs) { 308 | std::string sep; 309 | for (auto&& elem : rhs) { 310 | os << sep << elem; 311 | sep = " "; 312 | } 313 | 314 | return os; 315 | } 316 | 317 | inline namespace oqs_literals { 318 | /** 319 | * \brief User-defined literal operator for converting C-style strings to 320 | * oqs::bytes 321 | * \note The null terminator is not included 322 | * \param c_str C-style string 323 | * \param length C-style string length (deduced automatically by the compiler) 324 | * \return The byte representation of the input C-style string 325 | */ 326 | inline oqs::bytes operator""_bytes(const char* c_str, std::size_t length) { 327 | oqs::bytes result(length); 328 | for (std::size_t i = 0; i < length; ++i) 329 | result[i] = static_cast(c_str[i]); 330 | 331 | return result; 332 | } 333 | } // namespace oqs_literals 334 | 335 | #endif // COMMON_HPP_ 336 | -------------------------------------------------------------------------------- /include/oqs_cpp.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file oqs_cpp.hpp 3 | * \brief Main header file for the liboqs C++ wrapper 4 | */ 5 | 6 | #ifndef OQS_CPP_HPP_ 7 | #define OQS_CPP_HPP_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "common.hpp" 20 | 21 | /** 22 | * \namespace oqs 23 | * \brief Main namespace for the liboqs C++ wrapper 24 | */ 25 | namespace oqs { 26 | /** 27 | * \namespace oqs::C 28 | * \brief Namespace containing all of the oqs C functions, so they do not 29 | * pollute the oqs namespace 30 | */ 31 | namespace C { 32 | // Everything in liboqs has C linkage 33 | extern "C" { 34 | #include 35 | } 36 | } // namespace C 37 | 38 | /** 39 | * \class oqs::MechanismNotSupportedError 40 | * \brief Cryptographic scheme not supported 41 | */ 42 | class MechanismNotSupportedError : public std::runtime_error { 43 | public: 44 | /** 45 | * \brief Constructor 46 | * \param alg_name Cryptographic algorithm name 47 | */ 48 | explicit MechanismNotSupportedError(const std::string& alg_name) 49 | : std::runtime_error{"\"" + alg_name + "\"" + 50 | " is not supported by OQS"} {} 51 | }; // class MechanismNotSupportedError 52 | 53 | /** 54 | * \class oqs::MechanismNotEnabledError 55 | * \brief Cryptographic scheme not enabled 56 | */ 57 | class MechanismNotEnabledError : public std::runtime_error { 58 | public: 59 | /** 60 | * \brief Constructor 61 | * \param alg_name Cryptographic algorithm name 62 | */ 63 | explicit MechanismNotEnabledError(const std::string& alg_name) 64 | : std::runtime_error{"\"" + alg_name + "\"" + 65 | " is not enabled by OQS"} {} 66 | }; // class MechanismNotEnabledError 67 | 68 | /** 69 | * \class oqs::KEMs 70 | * \brief Singleton class, contains details about supported/enabled key exchange 71 | * mechanisms (KEMs) 72 | */ 73 | class KEMs final : public internal::Singleton { 74 | friend class internal::Singleton; 75 | 76 | /** 77 | * \brief Private default constructor 78 | * \note Use oqs::KEMs::get_instance() to create an instance 79 | */ 80 | KEMs() = default; 81 | 82 | public: 83 | /** 84 | * \brief Maximum number of supported KEM algorithms 85 | * \return Maximum number of supported KEM algorithms 86 | */ 87 | static std::size_t max_number_KEMs() { 88 | static std::size_t max_number_KEMs_ = C::OQS_KEM_alg_count(); 89 | 90 | return max_number_KEMs_; 91 | } 92 | 93 | /** 94 | * \brief Checks whether the KEM algorithm \a alg_name is supported 95 | * \param alg_name Cryptographic algorithm name 96 | * \return True if the KEM algorithm is supported, false otherwise 97 | */ 98 | static bool is_KEM_supported(const std::string& alg_name) { 99 | auto supported_KEMs = get_supported_KEMs(); 100 | 101 | return std::find(supported_KEMs.begin(), supported_KEMs.end(), 102 | alg_name) != supported_KEMs.end(); 103 | } 104 | 105 | /** 106 | * \brief Checks whether the KEM algorithm \a alg_name is enabled 107 | * \param alg_name Cryptographic algorithm name 108 | * \return True if the KEM algorithm is enabled, false otherwise 109 | */ 110 | static bool is_KEM_enabled(const std::string& alg_name) { 111 | return C::OQS_KEM_alg_is_enabled(alg_name.c_str()); 112 | } 113 | 114 | /** 115 | * \brief KEM algorithm name 116 | * \param alg_id Cryptographic algorithm numerical id 117 | * \return KEM algorithm name 118 | */ 119 | static std::string get_KEM_name(std::size_t alg_id) { 120 | if (alg_id >= max_number_KEMs()) 121 | throw std::out_of_range("Algorithm ID out of range"); 122 | 123 | return C::OQS_KEM_alg_identifier(alg_id); 124 | } 125 | 126 | /** 127 | * \brief Vector of supported KEM algorithms 128 | * \return Vector of supported KEM algorithms 129 | */ 130 | static const std::vector& get_supported_KEMs() { 131 | static std::vector supported_KEMs; 132 | static bool fnc_already_invoked = false; // function was already invoked 133 | 134 | if (!fnc_already_invoked) { 135 | for (std::size_t i = 0; i < max_number_KEMs(); ++i) 136 | supported_KEMs.emplace_back(get_KEM_name(i)); 137 | fnc_already_invoked = true; 138 | } 139 | 140 | return supported_KEMs; 141 | } 142 | 143 | /** 144 | * \brief Vector of enabled KEM algorithms 145 | * \return Vector of enabled KEM algorithms 146 | */ 147 | static const std::vector& get_enabled_KEMs() { 148 | static std::vector enabled_KEMs; 149 | static bool fnc_already_invoked = false; // function was already invoked 150 | 151 | if (!fnc_already_invoked) { 152 | for (auto&& elem : get_supported_KEMs()) 153 | if (is_KEM_enabled(elem)) 154 | enabled_KEMs.emplace_back(elem); 155 | fnc_already_invoked = true; 156 | } 157 | 158 | return enabled_KEMs; 159 | } 160 | }; // class KEMs 161 | 162 | /** 163 | * \class oqs::KeyEncapsulation 164 | * \brief Key encapsulation mechanisms 165 | */ 166 | class KeyEncapsulation { 167 | std::shared_ptr kem_{nullptr, [](C::OQS_KEM* p) { 168 | C::OQS_KEM_free(p); 169 | }}; ///< liboqs smart pointer to C::OQS_KEM 170 | bytes secret_key_{}; ///< secret key 171 | public: 172 | /** 173 | * \brief KEM algorithm details 174 | */ 175 | struct KeyEncapsulationDetails { 176 | std::string name; 177 | std::string version; 178 | std::size_t claimed_nist_level; 179 | bool is_ind_cca; 180 | std::size_t length_public_key; 181 | std::size_t length_secret_key; 182 | std::size_t length_ciphertext; 183 | std::size_t length_shared_secret; 184 | }; 185 | 186 | private: 187 | KeyEncapsulationDetails alg_details_{}; ///< KEM algorithm details 188 | 189 | public: 190 | /** 191 | * \brief Constructs an instance of oqs::KeyEncapsulation 192 | * \param alg_name Cryptographic algorithm name 193 | * \param secret_key Secret key (optional) 194 | */ 195 | explicit KeyEncapsulation(const std::string& alg_name, 196 | bytes secret_key = {}) 197 | : secret_key_{std::move(secret_key)} { 198 | // KEM not enabled 199 | if (!KEMs::is_KEM_enabled(alg_name)) { 200 | // Perhaps it's supported 201 | if (KEMs::is_KEM_supported(alg_name)) 202 | throw MechanismNotEnabledError(alg_name); 203 | else 204 | throw MechanismNotSupportedError(alg_name); 205 | } 206 | 207 | kem_.reset(C::OQS_KEM_new(alg_name.c_str()), 208 | [](C::OQS_KEM* p) { C::OQS_KEM_free(p); }); 209 | 210 | alg_details_.name = kem_->method_name; 211 | alg_details_.version = kem_->alg_version; 212 | alg_details_.claimed_nist_level = kem_->claimed_nist_level; 213 | alg_details_.is_ind_cca = kem_->ind_cca; 214 | alg_details_.length_public_key = kem_->length_public_key; 215 | alg_details_.length_secret_key = kem_->length_secret_key; 216 | alg_details_.length_ciphertext = kem_->length_ciphertext; 217 | alg_details_.length_shared_secret = kem_->length_shared_secret; 218 | } 219 | 220 | /** 221 | * \brief Default copy constructor 222 | */ 223 | KeyEncapsulation(const KeyEncapsulation&) = default; 224 | 225 | /** 226 | * \brief Default copy assignment operator 227 | * \return Reference to the current instance 228 | */ 229 | KeyEncapsulation& operator=(const KeyEncapsulation&) = default; 230 | 231 | /** 232 | * \brief Move constructor, guarantees that the rvalue secret key is always 233 | * zeroed 234 | * \param rhs oqs::KeyEncapsulation instance 235 | */ 236 | KeyEncapsulation(KeyEncapsulation&& rhs) noexcept 237 | : kem_{std::move(rhs.kem_)}, alg_details_{std::move(rhs.alg_details_)} { 238 | // Paranoid move via copy/clean/resize, see 239 | // https://stackoverflow.com/questions/55054187/can-i-resize-a-vector-that-was-moved-from 240 | secret_key_ = rhs.secret_key_; // copy 241 | // Clean (zero) 242 | C::OQS_MEM_cleanse(rhs.secret_key_.data(), rhs.secret_key_.size()); 243 | rhs.secret_key_.resize(0); // resize 244 | } 245 | /** 246 | * \brief Move assignment operator, guarantees that the rvalue secret key is 247 | * always zeroed 248 | * \param rhs oqs::KeyEncapsulation instance 249 | * \return Reference to the current instance 250 | */ 251 | KeyEncapsulation& operator=(KeyEncapsulation&& rhs) noexcept { 252 | kem_ = std::move(rhs.kem_); 253 | alg_details_ = std::move(rhs.alg_details_); 254 | 255 | // Paranoid move via copy/clean/resize, see 256 | // https://stackoverflow.com/questions/55054187/can-i-resize-a-vector-that-was-moved-from 257 | secret_key_ = rhs.secret_key_; // copy 258 | // Clean (zero) 259 | C::OQS_MEM_cleanse(rhs.secret_key_.data(), rhs.secret_key_.size()); 260 | rhs.secret_key_.resize(0); // resize 261 | 262 | return *this; 263 | } 264 | 265 | /** 266 | * \brief Virtual default destructor 267 | */ 268 | virtual ~KeyEncapsulation() { 269 | if (!secret_key_.empty()) 270 | C::OQS_MEM_cleanse(secret_key_.data(), secret_key_.size()); 271 | } 272 | 273 | /** 274 | * \brief KEM algorithm details, lvalue overload 275 | * \return KEM algorithm details 276 | */ 277 | const KeyEncapsulationDetails& get_details() const& { return alg_details_; } 278 | 279 | /** 280 | * \brief KEM algorithm details, rvalue overload 281 | * \return KEM algorithm details 282 | */ 283 | KeyEncapsulationDetails get_details() const&& { return alg_details_; } 284 | 285 | /** 286 | * \brief Generate public key/secret key pair 287 | * \return Public key 288 | */ 289 | bytes generate_keypair() { 290 | bytes public_key(alg_details_.length_public_key, 0); 291 | secret_key_ = bytes(alg_details_.length_secret_key, 0); 292 | 293 | OQS_STATUS rv_ = C::OQS_KEM_keypair(kem_.get(), public_key.data(), 294 | secret_key_.data()); 295 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 296 | throw std::runtime_error("Can not generate keypair"); 297 | 298 | return public_key; 299 | } 300 | 301 | /** 302 | * \brief Export secret key 303 | * \return Secret key 304 | */ 305 | bytes export_secret_key() const { return secret_key_; } 306 | 307 | /** 308 | * \brief Encapsulate secret 309 | * \param public_key Public key 310 | * \return Pair consisting of 1) ciphertext, and 2) shared secret 311 | */ 312 | std::pair encap_secret(const bytes& public_key) const { 313 | if (public_key.size() != alg_details_.length_public_key) 314 | throw std::runtime_error("Incorrect public key length"); 315 | 316 | bytes ciphertext(alg_details_.length_ciphertext, 0); 317 | bytes shared_secret(alg_details_.length_shared_secret, 0); 318 | OQS_STATUS rv_ = 319 | C::OQS_KEM_encaps(kem_.get(), ciphertext.data(), 320 | shared_secret.data(), public_key.data()); 321 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 322 | throw std::runtime_error("Can not encapsulate secret"); 323 | 324 | return std::make_pair(ciphertext, shared_secret); 325 | } 326 | 327 | /** 328 | * \brief Decapsulate secret 329 | * \param ciphertext Ciphertext 330 | * \return Shared secret 331 | */ 332 | bytes decap_secret(const bytes& ciphertext) const { 333 | if (ciphertext.size() != alg_details_.length_ciphertext) 334 | throw std::runtime_error("Incorrect ciphertext length"); 335 | 336 | if (secret_key_.size() != alg_details_.length_secret_key) 337 | throw std::runtime_error( 338 | "Incorrect secret key length, make sure you " 339 | "specify one in the constructor or run " 340 | "oqs::Signature::generate_keypair()"); 341 | 342 | bytes shared_secret(alg_details_.length_shared_secret, 0); 343 | OQS_STATUS rv_ = 344 | C::OQS_KEM_decaps(kem_.get(), shared_secret.data(), 345 | ciphertext.data(), secret_key_.data()); 346 | 347 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 348 | throw std::runtime_error("Can not decapsulate secret"); 349 | 350 | return shared_secret; 351 | } 352 | 353 | /** 354 | * \brief std::ostream extraction operator for the KEM algorithm details 355 | * \param os Output stream 356 | * \param rhs Algorithm details instance 357 | * \return Reference to the output stream 358 | */ 359 | friend std::ostream& operator<<(std::ostream& os, 360 | const KeyEncapsulationDetails& rhs) { 361 | os << "Name: " << rhs.name << '\n'; 362 | os << "Version: " << rhs.version << '\n'; 363 | os << "Claimed NIST level: " << rhs.claimed_nist_level << '\n'; 364 | os << "Is IND_CCA: " << rhs.is_ind_cca << '\n'; 365 | os << "Length public key (bytes): " << rhs.length_public_key << '\n'; 366 | os << "Length secret key (bytes): " << rhs.length_secret_key << '\n'; 367 | os << "Length ciphertext (bytes): " << rhs.length_ciphertext << '\n'; 368 | os << "Length shared secret (bytes): " << rhs.length_shared_secret; 369 | 370 | return os; 371 | } 372 | 373 | /** 374 | * \brief std::ostream extraction operator for oqs::KeyEncapsulation 375 | * \param os Output stream 376 | * \param rhs oqs::KeyEncapsulation instance 377 | * \return Reference to the output stream 378 | */ 379 | friend std::ostream& operator<<(std::ostream& os, 380 | const KeyEncapsulation& rhs) { 381 | return os << "Key encapsulation mechanism: " << rhs.alg_details_.name; 382 | } 383 | }; // class KeyEncapsulation 384 | 385 | /** 386 | * \class oqs::Sigs 387 | * \brief Singleton class, contains details about supported/enabled signature 388 | * mechanisms 389 | */ 390 | class Sigs final : public internal::Singleton { 391 | friend class internal::Singleton; 392 | 393 | /** 394 | * \brief Private default constructor 395 | * \note Use oqs::Sigs::get_instance() to create an instance 396 | */ 397 | Sigs() = default; 398 | 399 | public: 400 | /** 401 | * \brief Maximum number of supported signature algorithms 402 | * \return Maximum number of supported signature algorithms 403 | */ 404 | static std::size_t max_number_sigs() { 405 | static std::size_t max_number_sigs_ = C::OQS_SIG_alg_count(); 406 | 407 | return max_number_sigs_; 408 | } 409 | 410 | /** 411 | * \brief Checks whether the signature algorithm \a alg_name is supported 412 | * \param alg_name Cryptographic algorithm name 413 | * \return True if the signature algorithm is supported, false otherwise 414 | */ 415 | static bool is_sig_supported(const std::string& alg_name) { 416 | auto supported_sigs = get_supported_sigs(); 417 | 418 | return std::find(supported_sigs.begin(), supported_sigs.end(), 419 | alg_name) != supported_sigs.end(); 420 | } 421 | 422 | /** 423 | * \brief Checks whether the signature algorithm \a alg_name is enabled 424 | * \param alg_name Cryptographic algorithm name 425 | * \return True if the signature algorithm is enabled, false otherwise 426 | */ 427 | static bool is_sig_enabled(const std::string& alg_name) { 428 | return C::OQS_SIG_alg_is_enabled(alg_name.c_str()); 429 | } 430 | 431 | /** 432 | * \brief Signature algorithm name 433 | * \param alg_id Cryptographic algorithm numerical id 434 | * \return Signature algorithm name 435 | */ 436 | static std::string get_sig_name(std::size_t alg_id) { 437 | if (alg_id >= max_number_sigs()) 438 | throw std::out_of_range("Algorithm ID out of range"); 439 | 440 | return C::OQS_SIG_alg_identifier(alg_id); 441 | } 442 | 443 | /** 444 | * \brief Vector of supported signature algorithms 445 | * \return Vector of supported signature algorithms 446 | */ 447 | static const std::vector& get_supported_sigs() { 448 | static std::vector supported_sigs; 449 | static bool fnc_already_invoked = false; 450 | 451 | if (!fnc_already_invoked) { 452 | for (std::size_t i = 0; i < max_number_sigs(); ++i) 453 | supported_sigs.emplace_back(get_sig_name(i)); 454 | fnc_already_invoked = true; 455 | } 456 | 457 | return supported_sigs; 458 | } 459 | 460 | /** 461 | * \brief Vector of enabled signature algorithms 462 | * \return Vector of enabled signature algorithms 463 | */ 464 | static const std::vector& get_enabled_sigs() { 465 | static std::vector enabled_sigs; 466 | static bool fnc_already_invoked = false; 467 | 468 | if (!fnc_already_invoked) { 469 | for (auto&& elem : get_supported_sigs()) 470 | if (is_sig_enabled(elem)) 471 | enabled_sigs.emplace_back(elem); 472 | fnc_already_invoked = true; 473 | } 474 | 475 | return enabled_sigs; 476 | } 477 | }; // class Sigs 478 | 479 | /** 480 | * \class oqs::Signature 481 | * \brief Signature mechanisms 482 | */ 483 | class Signature { 484 | std::shared_ptr sig_{nullptr, [](C::OQS_SIG* p) { 485 | C::OQS_SIG_free(p); 486 | }}; ///< liboqs smart pointer to C::OQS_SIG 487 | bytes secret_key_{}; ///< secret key 488 | 489 | public: 490 | /** 491 | * \brief Signature algorithm details 492 | */ 493 | struct SignatureDetails { 494 | std::string name; 495 | std::string version; 496 | std::size_t claimed_nist_level; 497 | bool is_euf_cma; 498 | bool sig_with_ctx_support; 499 | std::size_t length_public_key; 500 | std::size_t length_secret_key; 501 | std::size_t max_length_signature; 502 | }; 503 | 504 | private: 505 | SignatureDetails alg_details_{}; ///< Signature algorithm details 506 | 507 | public: 508 | /** 509 | * \brief Constructs an instance of oqs::Signature 510 | * \param alg_name Cryptographic algorithm name 511 | * \param secret_key Secret key (optional) 512 | */ 513 | explicit Signature(const std::string& alg_name, bytes secret_key = {}) 514 | : secret_key_{std::move(secret_key)} { 515 | // signature not enabled 516 | if (!Sigs::is_sig_enabled(alg_name)) { 517 | // perhaps it's supported 518 | if (Sigs::is_sig_supported(alg_name)) 519 | throw MechanismNotEnabledError(alg_name); 520 | else 521 | throw MechanismNotSupportedError(alg_name); 522 | } 523 | 524 | sig_.reset(C::OQS_SIG_new(alg_name.c_str()), 525 | [](C::OQS_SIG* p) { C::OQS_SIG_free(p); }); 526 | 527 | alg_details_.name = sig_->method_name; 528 | alg_details_.version = sig_->alg_version; 529 | alg_details_.claimed_nist_level = sig_->claimed_nist_level; 530 | alg_details_.is_euf_cma = sig_->euf_cma; 531 | alg_details_.sig_with_ctx_support = sig_->sig_with_ctx_support; 532 | alg_details_.length_public_key = sig_->length_public_key; 533 | alg_details_.length_secret_key = sig_->length_secret_key; 534 | alg_details_.max_length_signature = sig_->length_signature; 535 | } 536 | 537 | /** 538 | * \brief Default copy constructor 539 | */ 540 | Signature(const Signature&) = default; 541 | 542 | /** 543 | * \brief Default copy assignment operator 544 | * \return Reference to the current instance 545 | */ 546 | Signature& operator=(const Signature&) = default; 547 | 548 | /** 549 | * \brief Move constructor, guarantees that the rvalue secret key is always 550 | * zeroed 551 | * \param rhs oqs::Signature instance 552 | */ 553 | Signature(Signature&& rhs) noexcept 554 | : sig_{std::move(rhs.sig_)}, alg_details_{std::move(rhs.alg_details_)} { 555 | // Paranoid move via copy/clean/resize, see 556 | // https://stackoverflow.com/questions/55054187/can-i-resize-a-vector-that-was-moved-from 557 | secret_key_ = rhs.secret_key_; // copy 558 | // Clean (zero) 559 | C::OQS_MEM_cleanse(rhs.secret_key_.data(), rhs.secret_key_.size()); 560 | rhs.secret_key_.resize(0); // resize 561 | } 562 | /** 563 | * \brief Move assignment operator, guarantees that the rvalue secret key is 564 | * always zeroed 565 | * \param rhs oqs::Signature instance 566 | * \return Reference to the current instance 567 | */ 568 | Signature& operator=(Signature&& rhs) noexcept { 569 | sig_ = std::move(rhs.sig_); 570 | alg_details_ = std::move(rhs.alg_details_); 571 | 572 | // Paranoid move via copy/clean/resize, see 573 | // https://stackoverflow.com/questions/55054187/can-i-resize-a-vector-that-was-moved-from 574 | secret_key_ = rhs.secret_key_; // copy 575 | // Clean (zero) 576 | C::OQS_MEM_cleanse(rhs.secret_key_.data(), rhs.secret_key_.size()); 577 | rhs.secret_key_.resize(0); // resize 578 | 579 | return *this; 580 | } 581 | 582 | /** 583 | * \brief Virtual default destructor 584 | */ 585 | virtual ~Signature() { 586 | if (!secret_key_.empty()) 587 | C::OQS_MEM_cleanse(secret_key_.data(), secret_key_.size()); 588 | } 589 | 590 | /** 591 | * \brief Signature algorithm details, lvalue overload 592 | * \return Signature algorithm details 593 | */ 594 | const SignatureDetails& get_details() const& { return alg_details_; } 595 | 596 | /** 597 | * \brief Signature algorithm details, rvalue overload 598 | * \return Signature algorithm details 599 | */ 600 | SignatureDetails get_details() const&& { return alg_details_; } 601 | 602 | /** 603 | * \brief Generate public key/secret key pair 604 | * \return Public key 605 | */ 606 | bytes generate_keypair() { 607 | bytes public_key(get_details().length_public_key, 0); 608 | secret_key_ = bytes(alg_details_.length_secret_key, 0); 609 | 610 | OQS_STATUS rv_ = C::OQS_SIG_keypair(sig_.get(), public_key.data(), 611 | secret_key_.data()); 612 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 613 | throw std::runtime_error("Can not generate keypair"); 614 | 615 | return public_key; 616 | } 617 | 618 | /** 619 | * \brief Export secret key 620 | * \return Secret key 621 | */ 622 | bytes export_secret_key() const { return secret_key_; } 623 | 624 | /** 625 | * \brief Sign message 626 | * \param message Message 627 | * \return Message signature 628 | */ 629 | bytes sign(const bytes& message) const { 630 | if (secret_key_.size() != alg_details_.length_secret_key) 631 | throw std::runtime_error( 632 | "Incorrect secret key length, make sure you " 633 | "specify one in the constructor or run " 634 | "oqs::Signature::generate_keypair()"); 635 | 636 | bytes signature(alg_details_.max_length_signature, 0); 637 | 638 | std::size_t len_sig; 639 | OQS_STATUS rv_ = 640 | C::OQS_SIG_sign(sig_.get(), signature.data(), &len_sig, 641 | message.data(), message.size(), secret_key_.data()); 642 | 643 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 644 | throw std::runtime_error("Can not sign message"); 645 | 646 | signature.resize(len_sig); 647 | 648 | return signature; 649 | } 650 | 651 | /** 652 | * \brief Sign message with context string 653 | * \param message Message 654 | * \param context Context string 655 | * \return Message signature 656 | */ 657 | bytes sign_with_ctx_str(const bytes& message, const bytes& context) const { 658 | if (!context.empty() && !alg_details_.sig_with_ctx_support) 659 | throw std::runtime_error( 660 | "Signing with context string not supported"); 661 | 662 | if (secret_key_.size() != alg_details_.length_secret_key) 663 | throw std::runtime_error( 664 | "Incorrect secret key length, make sure you " 665 | "specify one in the constructor or run " 666 | "oqs::Signature::generate_keypair()"); 667 | 668 | bytes signature(alg_details_.max_length_signature, 0); 669 | 670 | std::size_t len_sig; 671 | OQS_STATUS rv_ = C::OQS_SIG_sign_with_ctx_str( 672 | sig_.get(), signature.data(), &len_sig, message.data(), 673 | message.size(), context.data(), context.size(), secret_key_.data()); 674 | 675 | if (rv_ != OQS_STATUS::OQS_SUCCESS) 676 | throw std::runtime_error( 677 | "Can not sign message with context string"); 678 | 679 | signature.resize(len_sig); 680 | 681 | return signature; 682 | } 683 | 684 | /** 685 | * \brief Verify signature 686 | * \param message Message 687 | * \param signature Signature 688 | * \param public_key Public key 689 | * \return True if the signature is valid, false otherwise 690 | */ 691 | bool verify(const bytes& message, const bytes& signature, 692 | const bytes& public_key) const { 693 | if (public_key.size() != alg_details_.length_public_key) 694 | throw std::runtime_error("Incorrect public key length"); 695 | 696 | if (signature.size() > alg_details_.max_length_signature) 697 | throw std::runtime_error("Incorrect signature size"); 698 | 699 | OQS_STATUS rv_ = C::OQS_SIG_verify(sig_.get(), message.data(), 700 | message.size(), signature.data(), 701 | signature.size(), public_key.data()); 702 | 703 | return rv_ == OQS_STATUS::OQS_SUCCESS; 704 | } 705 | 706 | /** 707 | * \brief Verify signature with context string 708 | * \param message Message 709 | * \param signature Signature 710 | * \param context Context string 711 | * \param public_key Public key 712 | * \return True if the signature is valid, false otherwise 713 | */ 714 | bool verify_with_ctx_str(const bytes& message, const bytes& signature, 715 | const bytes& context, 716 | const bytes& public_key) const { 717 | if (!context.empty() && !alg_details_.sig_with_ctx_support) 718 | throw std::runtime_error( 719 | "Verifying with context string not supported"); 720 | 721 | if (public_key.size() != alg_details_.length_public_key) 722 | throw std::runtime_error("Incorrect public key length"); 723 | 724 | if (signature.size() > alg_details_.max_length_signature) 725 | throw std::runtime_error("Incorrect signature size"); 726 | 727 | OQS_STATUS rv_ = C::OQS_SIG_verify_with_ctx_str( 728 | sig_.get(), message.data(), message.size(), signature.data(), 729 | signature.size(), context.data(), context.size(), 730 | public_key.data()); 731 | 732 | return rv_ == OQS_STATUS::OQS_SUCCESS; 733 | } 734 | 735 | /** 736 | * \brief std::ostream extraction operator for the signature algorithm 737 | * details 738 | * \param os Output stream 739 | * \param rhs Algorithm details instance 740 | * \return Reference to the output stream 741 | */ 742 | friend std::ostream& operator<<(std::ostream& os, 743 | const SignatureDetails& rhs) { 744 | os << "Name: " << rhs.name << '\n'; 745 | os << "Version: " << rhs.version << '\n'; 746 | os << "Claimed NIST level: " << rhs.claimed_nist_level << '\n'; 747 | os << "Is EUF_CMA: " << rhs.is_euf_cma << '\n'; 748 | os << "Supports context string: " << rhs.sig_with_ctx_support << '\n'; 749 | os << "Length public key (bytes): " << rhs.length_public_key << '\n'; 750 | os << "Length secret key (bytes): " << rhs.length_secret_key << '\n'; 751 | os << "Maximum length signature (bytes): " << rhs.max_length_signature; 752 | 753 | return os; 754 | } 755 | 756 | /** 757 | * \brief std::ostream extraction operator for oqs::Signature 758 | * \param os Output stream 759 | * \param rhs oqs::Signature instance 760 | * \return Reference to the output stream 761 | */ 762 | friend std::ostream& operator<<(std::ostream& os, const Signature& rhs) { 763 | return os << "Signature mechanism: " << rhs.alg_details_.name; 764 | } 765 | }; // class Signature 766 | 767 | namespace internal { 768 | /** 769 | * \class oqs::internal::Init 770 | * \brief liboqs initialization 771 | */ 772 | class Init final : public internal::Singleton { 773 | friend class internal::Singleton; 774 | /** 775 | * \brief Private default constructor 776 | * \note Use oqs::internal::Init::get_instance() to create an instance 777 | */ 778 | Init() { 779 | C::OQS_init(); 780 | std::string oqs_ver = oqs_version(); 781 | std::string oqs_ver_major = std::get<0>(version(oqs_ver)); 782 | std::string oqs_ver_minor = std::get<1>(version(oqs_ver)); 783 | 784 | std::string oqs_cpp_ver = oqs_cpp_version(); 785 | std::string oqs_cpp_ver_major = std::get<0>(version(oqs_cpp_ver)); 786 | std::string oqs_cpp_ver_minor = std::get<1>(version(oqs_cpp_ver)); 787 | 788 | if (!(oqs_ver_major == oqs_cpp_ver_major && 789 | oqs_ver_minor == oqs_cpp_ver_minor)) { 790 | std::cerr << "Warning! liboqs version (major, minor) " << oqs_ver 791 | << " differs from liboqs-cpp version " << oqs_cpp_ver 792 | << std::endl; 793 | } 794 | } 795 | }; 796 | static const Init& init_ = Init::get_instance(); ///< liboqs initialization 797 | // Instantiate the KEMs and Sigs singletons (if ever needed) 798 | static const KEMs& kems_ = 799 | KEMs::get_instance(); ///< instantiates the KEMs singleton 800 | static const Sigs& sigs_ = 801 | Sigs::get_instance(); ///< instantiates the Sigs singleton 802 | } // namespace internal 803 | } // namespace oqs 804 | 805 | #endif // OQS_CPP_HPP_ 806 | -------------------------------------------------------------------------------- /include/rand/rand.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file rand/rand.hpp 3 | * \brief Provides support for various RNG-related functions 4 | */ 5 | 6 | #ifndef RAND_RAND_HPP_ 7 | #define RAND_RAND_HPP_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "common.hpp" 16 | 17 | namespace oqs { 18 | namespace C { 19 | // everything in liboqs has C linkage 20 | extern "C" { 21 | #include 22 | } 23 | } // namespace C 24 | 25 | /** 26 | * \namespace rand 27 | * \brief Namespace containing RNG-related functions 28 | */ 29 | namespace rand { 30 | /** 31 | * \brief Generates \a bytes_to_read random bytes 32 | * \note This implementation uses either the default RNG algorithm ("system"), 33 | * or whichever algorithm has been selected by 34 | * oqs::rand::randombytes_switch_algorithm() 35 | * \param bytes_to_read The number of random bytes to generate 36 | * \return Vector of random bytes 37 | */ 38 | inline bytes randombytes(std::size_t bytes_to_read) { 39 | bytes result(bytes_to_read); 40 | C::OQS_randombytes(result.data(), bytes_to_read); 41 | return result; 42 | } 43 | 44 | /** 45 | * \brief Generates \a bytes_to_read random bytes in-place 46 | * \note This implementation uses either the default RNG algorithm ("system"), 47 | * or whichever algorithm has been selected by 48 | * oqs::rand::randombytes_switch_algorithm() 49 | * \note \a bytes_to_read must not exceed the size of \a random_array 50 | * \param [in] bytes_to_read The number of random bytes to generate 51 | * \param [out] random_array Output vector of random bytes 52 | */ 53 | inline void randombytes(bytes& random_array, std::size_t bytes_to_read) { 54 | if (bytes_to_read > random_array.size()) 55 | throw(std::out_of_range( 56 | "bytes_to_read exceeds the size of random_array")); 57 | C::OQS_randombytes(random_array.data(), bytes_to_read); 58 | } 59 | 60 | /** 61 | * \brief Switches the core OQS_randombytes to use the specified algorithm 62 | * \see liboqs header for more details. 63 | * \param alg_name Algorithm name, possible values are "system", "OpenSSL", or 64 | * the corresponding macros OQS_RAND_alg_system and OQS_RAND_alg_openssl, 65 | * respectively. 66 | */ 67 | inline void randombytes_switch_algorithm(const std::string& alg_name) { 68 | if (C::OQS_randombytes_switch_algorithm(alg_name.c_str()) != C::OQS_SUCCESS) 69 | throw std::runtime_error("Can not switch algorithm"); 70 | } 71 | 72 | /** 73 | * \brief Switches oqs::rand::randombytes() to use the given function 74 | * \note This allows additional custom RNGs besides the provided ones. 75 | * \param algorithm_ptr Pointer to RNG function 76 | */ 77 | inline void randombytes_custom_algorithm(void (*algorithm_ptr)(uint8_t*, 78 | std::size_t)) { 79 | C::OQS_randombytes_custom_algorithm(algorithm_ptr); 80 | } 81 | } // namespace rand 82 | } // namespace oqs 83 | 84 | #endif // RAND_RAND_HPP_ 85 | -------------------------------------------------------------------------------- /prettyprint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Code beautifier with clang-format 4 | # Recursively parses the directories passed as command line arguments 5 | 6 | # Arguments: 7 | # 8 | # $@ - List of directories 9 | 10 | if test -z "$CLANG_FORMAT"; then 11 | echo "Please set the CLANG_FORMAT environment variable to point to the \ 12 | location of clang-format" 13 | exit 1 14 | else 15 | if ! [ -x "$(command -v "$CLANG_FORMAT")" ]; then 16 | echo "Error: $CLANG_FORMAT executable not found." >&2 17 | exit 1 18 | fi 19 | echo "Code formatting with '$CLANG_FORMAT' the directories:" 20 | fi 21 | 22 | for directory in "$@"; do 23 | echo "$directory" 24 | find "$directory" \( -iname '*.cpp' -o -iname '*.c' -o -iname '*.h' \ 25 | -o -iname '*.hpp' \) -exec "$CLANG_FORMAT" -style=file -i {} + 26 | done 27 | -------------------------------------------------------------------------------- /unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GoogleTest) 2 | set(TARGET_NAME "unit_tests") 3 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 4 | 5 | # GoogleTest 6 | include(FetchContent) 7 | message(STATUS "Fetching GoogleTest...") 8 | FetchContent_Declare( 9 | googletest 10 | GIT_REPOSITORY https://github.com/google/googletest.git 11 | GIT_TAG main 12 | GIT_SHALLOW TRUE 13 | GIT_PROGRESS TRUE) 14 | # For Windows: Prevent overriding the parent project's compiler/linker settings, 15 | # and add bigobj option 16 | if(MSVC) 17 | add_compile_options(-bigobj) 18 | set(gtest_force_shared_crt 19 | ON 20 | CACHE BOOL "" FORCE) 21 | endif() 22 | FetchContent_MakeAvailable(googletest) 23 | 24 | aux_source_directory(tests TEST_FILES) 25 | 26 | add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL tests/main.cpp) 27 | add_dependencies(unit_tests ${TARGET_NAME}) 28 | 29 | cmake_policy(SET CMP0076 NEW) 30 | 31 | # Build all tests in ${TEST_FILES} 32 | foreach(file ${TEST_FILES}) 33 | target_sources(${TARGET_NAME} PUBLIC ${file}) 34 | endforeach() 35 | 36 | target_link_libraries(${TARGET_NAME} PUBLIC liboqs-cpp oqs gmock) 37 | 38 | gtest_discover_tests(${TARGET_NAME}) 39 | -------------------------------------------------------------------------------- /unit_tests/tests/main.cpp: -------------------------------------------------------------------------------- 1 | // Unit testing entry point 2 | 3 | #include "gtest/gtest.h" 4 | 5 | int main(int argc, char** argv) { 6 | ::testing::InitGoogleTest(&argc, argv); 7 | return RUN_ALL_TESTS(); 8 | } 9 | -------------------------------------------------------------------------------- /unit_tests/tests/test_kem.cpp: -------------------------------------------------------------------------------- 1 | // Unit testing oqs::KeyEncapsulation 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "oqs_cpp.hpp" 12 | #include "rand/rand.hpp" 13 | 14 | // no_thread_KEM_patterns lists KEM patterns that have issues running in a 15 | // separate thread 16 | static std::vector no_thread_KEM_patterns{"Classic-McEliece", 17 | "HQC-256"}; 18 | 19 | // used for thread-safe console output 20 | static std::mutex mu; 21 | 22 | void test_kem_correctness(const std::string& kem_name) { 23 | { 24 | std::lock_guard lg{mu}; 25 | std::cout << "Correctness - " << kem_name << std::endl; 26 | } 27 | oqs::KeyEncapsulation client{kem_name}; 28 | oqs::bytes client_public_key = client.generate_keypair(); 29 | oqs::KeyEncapsulation server{kem_name}; 30 | oqs::bytes ciphertext, shared_secret_server; 31 | std::tie(ciphertext, shared_secret_server) = 32 | server.encap_secret(client_public_key); 33 | oqs::bytes shared_secret_client = client.decap_secret(ciphertext); 34 | bool is_valid = (shared_secret_client == shared_secret_server); 35 | if (!is_valid) 36 | std::cerr << kem_name << ": shared secrets do not coincide" 37 | << std::endl; 38 | EXPECT_TRUE(is_valid); 39 | } 40 | 41 | void test_kem_wrong_ciphertext(const std::string& kem_name) { 42 | { 43 | std::lock_guard lg{mu}; 44 | std::cout << "Wrong ciphertext - " << kem_name << std::endl; 45 | } 46 | oqs::KeyEncapsulation client{kem_name}; 47 | oqs::bytes client_public_key = client.generate_keypair(); 48 | oqs::KeyEncapsulation server{kem_name}; 49 | oqs::bytes ciphertext, shared_secret_server; 50 | std::tie(ciphertext, shared_secret_server) = 51 | server.encap_secret(client_public_key); 52 | oqs::bytes wrong_ciphertext = oqs::rand::randombytes(ciphertext.size()); 53 | oqs::bytes shared_secret_client; 54 | try { 55 | // this line should throw 56 | shared_secret_client = client.decap_secret(wrong_ciphertext); 57 | // if not, test should fail 58 | bool is_valid = (shared_secret_client == shared_secret_server); 59 | if (is_valid) 60 | std::cerr << kem_name << ": shared secrets should not coincide" 61 | << std::endl; 62 | EXPECT_FALSE(is_valid); 63 | } catch (std::exception& e) { 64 | if (e.what() == std::string{"Can not decapsulate secret"}) 65 | return; 66 | else 67 | throw; // this is another un-expected exception 68 | } 69 | } 70 | 71 | TEST(oqs_KeyEncapsulation, Correctness) { 72 | std::vector thread_pool; 73 | std::vector enabled_KEMs = oqs::KEMs::get_enabled_KEMs(); 74 | // first test KEMs that belong to no_thread_KEM_patterns[] in the main 75 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 76 | // too small in macOS (512Kb for threads) 77 | for (auto&& kem_name : enabled_KEMs) { 78 | for (auto&& no_thread_kem : no_thread_KEM_patterns) { 79 | if (kem_name.find(no_thread_kem) != std::string::npos) { 80 | test_kem_correctness(kem_name); 81 | } 82 | } 83 | } 84 | // test the remaining KEMs in separate threads 85 | for (auto&& kem_name : enabled_KEMs) { 86 | bool test_in_thread = true; 87 | for (auto&& no_thread_kem : no_thread_KEM_patterns) { 88 | if (kem_name.find(no_thread_kem) != std::string::npos) { 89 | test_in_thread = false; 90 | break; 91 | } 92 | } 93 | if (test_in_thread) 94 | thread_pool.emplace_back(test_kem_correctness, kem_name); 95 | } 96 | // join the rest of the threads 97 | for (auto&& elem : thread_pool) 98 | elem.join(); 99 | } 100 | 101 | TEST(oqs_KeyEncapsulation, WrongCiphertext) { 102 | std::vector thread_pool; 103 | std::vector enabled_KEMs = oqs::KEMs::get_enabled_KEMs(); 104 | // first test KEMs that belong to no_thread_KEM_patterns[] in the main 105 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 106 | // too small in macOS (512Kb for threads) 107 | for (auto&& kem_name : enabled_KEMs) { 108 | for (auto&& no_thread_kem : no_thread_KEM_patterns) { 109 | if (kem_name.find(no_thread_kem) != std::string::npos) { 110 | test_kem_wrong_ciphertext(kem_name); 111 | } 112 | } 113 | } 114 | // test the remaining KEMs in separate threads 115 | for (auto&& kem_name : enabled_KEMs) { 116 | bool test_in_thread = true; 117 | for (auto&& no_thread_kem : no_thread_KEM_patterns) { 118 | if (kem_name.find(no_thread_kem) != std::string::npos) { 119 | test_in_thread = false; 120 | break; 121 | } 122 | } 123 | if (test_in_thread) 124 | thread_pool.emplace_back(test_kem_wrong_ciphertext, kem_name); 125 | } 126 | // join the rest of the threads 127 | for (auto&& elem : thread_pool) 128 | elem.join(); 129 | } 130 | 131 | TEST(oqs_KeyEncapsulation, NotSupported) { 132 | EXPECT_THROW(oqs::KeyEncapsulation{"unsupported_kem"}, 133 | oqs::MechanismNotSupportedError); 134 | } 135 | -------------------------------------------------------------------------------- /unit_tests/tests/test_sig.cpp: -------------------------------------------------------------------------------- 1 | // Unit testing oqs::Signature 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "oqs_cpp.hpp" 11 | #include "rand/rand.hpp" 12 | 13 | // no_thread_sig_patterns lists sig patterns that have issues running in a 14 | // separate thread 15 | static std::vector no_thread_sig_patterns{ 16 | "cross-rsdp", "cross-rsdpg", "SPHINCS+", "Falcon", "MAYO"}; 17 | 18 | // used for thread-safe console output 19 | static std::mutex mu; 20 | 21 | void test_sig_correctness(const std::string& sig_name, const oqs::bytes& msg) { 22 | { 23 | std::lock_guard lg{mu}; 24 | std::cout << "Correctness - " << sig_name << std::endl; 25 | } 26 | oqs::Signature signer{sig_name}; 27 | oqs::bytes signer_public_key = signer.generate_keypair(); 28 | oqs::bytes signature = signer.sign(msg); 29 | oqs::Signature verifier{sig_name}; 30 | bool is_valid = verifier.verify(msg, signature, signer_public_key); 31 | if (!is_valid) 32 | std::cerr << sig_name << ": signature verification failed" << std::endl; 33 | EXPECT_TRUE(is_valid); 34 | } 35 | 36 | void test_sig_correctness_with_ctx_str(const std::string& sig_name, 37 | const oqs::bytes& msg) { 38 | oqs::Signature signer{sig_name}; 39 | if (!signer.get_details().sig_with_ctx_support) { 40 | return; 41 | } 42 | { 43 | std::lock_guard lg{mu}; 44 | std::cout << "Correctness with context string - " << sig_name 45 | << std::endl; 46 | } 47 | oqs::bytes context_str{"some context"_bytes}; 48 | oqs::bytes signer_public_key = signer.generate_keypair(); 49 | oqs::bytes signature = signer.sign_with_ctx_str(msg, context_str); 50 | oqs::Signature verifier{sig_name}; 51 | bool is_valid = verifier.verify_with_ctx_str(msg, signature, context_str, 52 | signer_public_key); 53 | if (!is_valid) 54 | std::cerr << sig_name 55 | << ": signature with context string verification failed" 56 | << std::endl; 57 | EXPECT_TRUE(is_valid); 58 | } 59 | 60 | void test_sig_wrong_signature(const std::string& sig_name, 61 | const oqs::bytes& msg) { 62 | { 63 | std::lock_guard lg{mu}; 64 | std::cout << "Wrong signature - " << sig_name << std::endl; 65 | } 66 | oqs::Signature signer{sig_name}; 67 | oqs::bytes signer_public_key = signer.generate_keypair(); 68 | oqs::bytes signature = signer.sign(msg); 69 | oqs::bytes wrong_signature = oqs::rand::randombytes(signature.size()); 70 | oqs::Signature verifier{sig_name}; 71 | bool is_valid = verifier.verify(msg, wrong_signature, signer_public_key); 72 | if (is_valid) 73 | std::cerr << sig_name << ": signature verification should have failed" 74 | << std::endl; 75 | EXPECT_FALSE(is_valid); 76 | } 77 | 78 | void test_sig_wrong_public_key(const std::string& sig_name, 79 | const oqs::bytes& msg) { 80 | { 81 | std::lock_guard lg{mu}; 82 | std::cout << "Wrong public key - " << sig_name << std::endl; 83 | } 84 | oqs::Signature signer{sig_name}; 85 | oqs::bytes signer_public_key = signer.generate_keypair(); 86 | oqs::bytes wrong_public_key = 87 | oqs::rand::randombytes(signer_public_key.size()); 88 | oqs::bytes signature = signer.sign(msg); 89 | oqs::Signature verifier{sig_name}; 90 | bool is_valid = verifier.verify(msg, signature, wrong_public_key); 91 | if (is_valid) 92 | std::cerr << sig_name << ": signature verification should have failed" 93 | << std::endl; 94 | EXPECT_FALSE(is_valid); 95 | } 96 | 97 | TEST(oqs_Signature, Correctness) { 98 | oqs::bytes message = "This is our favourite message to sign"_bytes; 99 | std::vector thread_pool; 100 | std::vector enabled_sigs = oqs::Sigs::get_enabled_sigs(); 101 | // first test sigs that belong to no_thread_sig_patterns[] in the main 102 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 103 | // too small in macOS (512Kb for threads) 104 | for (auto&& sig_name : enabled_sigs) { 105 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 106 | if (sig_name.find(no_thread_sig) != std::string::npos) { 107 | test_sig_correctness(sig_name, message); 108 | } 109 | } 110 | } 111 | // test the remaining sigs in separate threads 112 | for (auto&& sig_name : enabled_sigs) { 113 | bool test_in_thread = true; 114 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 115 | if (sig_name.find(no_thread_sig) != std::string::npos) { 116 | test_in_thread = false; 117 | break; 118 | } 119 | } 120 | if (test_in_thread) 121 | thread_pool.emplace_back(test_sig_correctness, sig_name, message); 122 | } 123 | // join the rest of the threads 124 | for (auto&& elem : thread_pool) 125 | elem.join(); 126 | } 127 | 128 | TEST(oqs_Signature, CorrectnessWithContextString) { 129 | oqs::bytes message = "This is our favourite message to sign"_bytes; 130 | std::vector thread_pool; 131 | std::vector enabled_sigs = oqs::Sigs::get_enabled_sigs(); 132 | // first test sigs that belong to no_thread_sig_patterns[] in the main 133 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 134 | // too small in macOS (512Kb for threads) 135 | for (auto&& sig_name : enabled_sigs) { 136 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 137 | if (sig_name.find(no_thread_sig) != std::string::npos) { 138 | test_sig_correctness_with_ctx_str(sig_name, message); 139 | } 140 | } 141 | } 142 | // test the remaining sigs in separate threads 143 | for (auto&& sig_name : enabled_sigs) { 144 | bool test_in_thread = true; 145 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 146 | if (sig_name.find(no_thread_sig) != std::string::npos) { 147 | test_in_thread = false; 148 | break; 149 | } 150 | } 151 | if (test_in_thread) 152 | thread_pool.emplace_back(test_sig_correctness_with_ctx_str, 153 | sig_name, message); 154 | } 155 | // join the rest of the threads 156 | for (auto&& elem : thread_pool) 157 | elem.join(); 158 | } 159 | 160 | TEST(oqs_Signature, WrongSignature) { 161 | oqs::bytes message = "This is our favourite message to sign"_bytes; 162 | std::vector thread_pool; 163 | std::vector enabled_sigs = oqs::Sigs::get_enabled_sigs(); 164 | // first test sigs that belong to no_thread_sig_patterns[] in the main 165 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 166 | // too small in macOS (512Kb for threads) 167 | for (auto&& sig_name : enabled_sigs) { 168 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 169 | if (sig_name.find(no_thread_sig) != std::string::npos) { 170 | test_sig_wrong_signature(sig_name, message); 171 | } 172 | } 173 | } 174 | // test the remaining sigs in separate threads 175 | for (auto&& sig_name : enabled_sigs) { 176 | bool test_in_thread = true; 177 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 178 | if (sig_name.find(no_thread_sig) != std::string::npos) { 179 | test_in_thread = false; 180 | break; 181 | } 182 | } 183 | if (test_in_thread) 184 | thread_pool.emplace_back(test_sig_wrong_signature, sig_name, 185 | message); 186 | } 187 | // join the rest of the threads 188 | for (auto&& elem : thread_pool) 189 | elem.join(); 190 | } 191 | 192 | TEST(oqs_Signature, WrongPublicKey) { 193 | oqs::bytes message = "This is our favourite message to sign"_bytes; 194 | std::vector thread_pool; 195 | std::vector enabled_sigs = oqs::Sigs::get_enabled_sigs(); 196 | // first test sigs that belong to no_thread_sig_patterns[] in the main 197 | // thread (stack size is 8Mb on macOS), due to issues with stack size being 198 | // too small in macOS (512Kb for threads) 199 | for (auto&& sig_name : enabled_sigs) { 200 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 201 | if (sig_name.find(no_thread_sig) != std::string::npos) { 202 | test_sig_wrong_public_key(sig_name, message); 203 | } 204 | } 205 | } 206 | // test the remaining sigs in separate threads 207 | for (auto&& sig_name : enabled_sigs) { 208 | bool test_in_thread = true; 209 | for (auto&& no_thread_sig : no_thread_sig_patterns) { 210 | if (sig_name.find(no_thread_sig) != std::string::npos) { 211 | test_in_thread = false; 212 | break; 213 | } 214 | } 215 | if (test_in_thread) 216 | thread_pool.emplace_back(test_sig_wrong_public_key, sig_name, 217 | message); 218 | } 219 | // join the rest of the threads 220 | for (auto&& elem : thread_pool) 221 | elem.join(); 222 | } 223 | 224 | TEST(oqs_Signature, NotSupported) { 225 | EXPECT_THROW(oqs::Signature{"unsupported_sig"}, 226 | oqs::MechanismNotSupportedError); 227 | } 228 | --------------------------------------------------------------------------------