├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build-test-mac.yml │ ├── build-test-ubuntu.yml │ ├── build-test-windows.yml │ └── generate_docs.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── get_nlohmann_json.cmake ├── get_version.cmake └── key_targets.cmake ├── docs ├── Doxygen │ ├── Doxyfile-prj.cfg │ ├── Doxyfile.cfg │ └── pyproject.toml ├── Sphinx │ ├── Makefile │ ├── make.bat │ └── source │ │ ├── How-to-use-it.rst │ │ ├── Identifiers.rst │ │ ├── _static │ │ └── .gitkeep │ │ ├── _templates │ │ └── .gitkeep │ │ ├── conf.py │ │ └── index.rst └── pyproject.toml ├── modules ├── CMakeLists.txt ├── crypto │ ├── CMakeLists.txt │ ├── include │ │ └── lcxx │ │ │ ├── crypto.hpp │ │ │ ├── encoding.hpp │ │ │ └── hash.hpp │ └── src │ │ ├── crypto.cpp │ │ ├── encoding.cpp │ │ └── hash.cpp ├── ident_utils │ ├── CMakeLists.txt │ ├── include │ │ └── ident_utils │ │ │ ├── common.hpp │ │ │ ├── cpu_utils.hpp │ │ │ └── os_utils.hpp │ └── src │ │ ├── cpu_utils_linux.cpp │ │ ├── cpu_utils_mac.cpp │ │ ├── cpu_utils_win.cpp │ │ ├── os_utils_linux.cpp │ │ ├── os_utils_mac.cpp │ │ └── os_utils_win.cpp ├── identifiers │ ├── CMakeLists.txt │ ├── include │ │ └── lcxx │ │ │ └── identifiers │ │ │ ├── common.hpp │ │ │ ├── hardware.hpp │ │ │ ├── identifier.hpp │ │ │ └── os.hpp │ └── src │ │ ├── hardware.cpp │ │ └── os.cpp ├── lcxx │ ├── CMakeLists.txt │ ├── include │ │ └── lcxx │ │ │ ├── lcxx.hpp │ │ │ ├── reader.hpp │ │ │ ├── verifier.hpp │ │ │ └── writer.hpp │ └── src │ │ ├── reader.cpp │ │ ├── verifier.cpp │ │ └── writer.cpp └── license │ ├── CMakeLists.txt │ └── include │ └── lcxx │ └── license.hpp ├── samples ├── CMakeLists.txt ├── ident_utils │ ├── CMakeLists.txt │ └── main.cpp ├── license_generator │ ├── CMakeLists.txt │ └── main.cpp └── license_verifier │ ├── CMakeLists.txt │ └── main.cpp ├── scripts ├── generate_headers.py └── version.hpp.in └── tests ├── CMakeLists.txt ├── common ├── fixtures.hpp └── keys.hpp ├── crypto ├── CMakeLists.txt ├── base64_test.cpp ├── crypto_test.cpp └── hash_test.cpp ├── identifiers ├── CMakeLists.txt └── identifiers_test.cpp ├── lcxx ├── CMakeLists.txt └── lcxx_test.cpp └── resources ├── private_key.rsa └── public_key /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | ColumnLimit: 120 4 | 5 | BreakBeforeBraces: Stroustrup 6 | BreakBeforeBinaryOperators: None 7 | 8 | PointerAlignment: Middle 9 | SpacesBeforeTrailingComments: 2 10 | BreakStringLiterals: true 11 | BreakConstructorInitializers: AfterColon 12 | UseTab: Never 13 | IndentWidth: 4 14 | AccessModifierOffset: -4 15 | AlignConsecutiveAssignments: true 16 | AlignConsecutiveDeclarations: true 17 | AlignConsecutiveMacros: true 18 | AlignEscapedNewlines: Right 19 | AlignTrailingComments: true 20 | NamespaceIndentation: All 21 | CompactNamespaces: true 22 | 23 | SpacesInConditionalStatement : true 24 | SpacesInAngles: true 25 | SpacesInParentheses: true 26 | 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: felixjulianheitmann 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: felixjulianheitmann 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build-test-mac.yml: -------------------------------------------------------------------------------- 1 | name: LCXX build & test - Mac 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | tags: 7 | - v*.*.* 8 | pull_request: 9 | branches: [ "develop", "main" ] 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | jobs: 16 | build-mac: 17 | runs-on: macos-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: configure 25 | run: | 26 | cmake -S . -B build -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.4 -DCMAKE_BUILD_TYPE=Release .. 27 | 28 | - name: build 29 | working-directory: build 30 | run: cmake --build . 31 | 32 | - name: list artifacts 33 | run: ls -R 34 | 35 | - uses: actions/upload-artifact@v4 36 | with: 37 | name: lcxx-tests-mac 38 | path: | 39 | ${{ github.workspace }}/build/tests/*/ 40 | retention-days: 3 41 | if-no-files-found: error 42 | 43 | tests-mac: 44 | needs: build-mac 45 | runs-on: macos-latest 46 | 47 | steps: 48 | - uses: actions/checkout@v4 49 | 50 | - uses: actions/download-artifact@v4 51 | with: 52 | name: lcxx-tests-mac 53 | 54 | - name: display artifacts 55 | run: ls -R 56 | 57 | - name: crypto-tests 58 | run: chmod +x crypto/crypto_test && ./crypto/crypto_test 59 | 60 | # - name: identifiers-tests 61 | # run: chmod +x identifiers/identifiers_test && ./identifiers/identifiers_test 62 | 63 | - name: lcxx-tests 64 | run: chmod +x lcxx/lcxx_test && ./lcxx/lcxx_test -------------------------------------------------------------------------------- /.github/workflows/build-test-ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: LCXX build & test - Ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | tags: 7 | - v*.*.* 8 | pull_request: 9 | branches: [ "develop", "main" ] 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | jobs: 16 | 17 | build-linux: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | 26 | - name: Get latest GCC 27 | uses: egor-tensin/setup-gcc@v1 28 | with: 29 | version: 11 30 | platform: x64 31 | 32 | - name: configure 33 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release .. 34 | 35 | - name: build 36 | working-directory: build 37 | run: cmake --build . 38 | 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | name: lcxx-tests-ubuntu 42 | path: | 43 | ${{ github.workspace }}/build/tests/*/ 44 | retention-days: 3 45 | if-no-files-found: error 46 | 47 | 48 | tests-linux: 49 | needs: build-linux 50 | runs-on: ubuntu-latest 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | 55 | - name: Get latest GCC (libstdc++ required) 56 | uses: egor-tensin/setup-gcc@v1 57 | with: 58 | version: 11 59 | platform: x64 60 | 61 | - uses: actions/download-artifact@v4 62 | with: 63 | name: lcxx-tests-ubuntu 64 | 65 | - name: display artifacts 66 | run: ls -R 67 | 68 | - name: crypto-tests 69 | run: chmod +x crypto/crypto_test && ./crypto/crypto_test 70 | 71 | - name: identifiers-tests 72 | run: chmod +x identifiers/identifiers_test && ./identifiers/identifiers_test 73 | 74 | - name: lcxx-tests 75 | run: chmod +x lcxx/lcxx_test && ./lcxx/lcxx_test -------------------------------------------------------------------------------- /.github/workflows/build-test-windows.yml: -------------------------------------------------------------------------------- 1 | name: LCXX build & test - Windows 2 | 3 | on: 4 | push: 5 | branches: [ "develop", "main" ] 6 | pull_request: 7 | branches: [ "develop", "main" ] 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | build-windows: 15 | runs-on: windows-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: install OpenSSL 23 | run: choco install openssl 24 | 25 | - name: configure 26 | run: | 27 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release .. 28 | 29 | - name: build 30 | working-directory: build 31 | run: cmake --build . --config Release 32 | 33 | - uses: actions/upload-artifact@v4 34 | with: 35 | name: lcxx-tests-windows 36 | path: | 37 | ${{ github.workspace }}/build/tests/*/ 38 | retention-days: 3 39 | if-no-files-found: error 40 | 41 | tests-windows: 42 | needs: build-windows 43 | runs-on: windows-latest 44 | 45 | steps: 46 | - uses: actions/checkout@v4 47 | 48 | # - name: install OpenSSL 49 | # run: choco install openssl 50 | 51 | - uses: actions/download-artifact@v4 52 | with: 53 | name: lcxx-tests-windows 54 | 55 | - name: display artifacts 56 | run: ls -R 57 | 58 | - name: crypto-tests 59 | run: ./crypto/Release/crypto_test.exe 60 | 61 | - name: lcxx-tests 62 | run: ./lcxx/Release/lcxx_test.exe -------------------------------------------------------------------------------- /.github/workflows/generate_docs.yml: -------------------------------------------------------------------------------- 1 | name: LCXX generate docs 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.*.* 7 | 8 | jobs: 9 | 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: get doxygen 16 | run: sudo apt-get update && sudo apt-get install -y doxygen 17 | 18 | - name: build doxygen 19 | working-directory: docs/Doxygen 20 | run: doxygen Doxyfile-prj.cfg 21 | 22 | - name: get sphinx + deps 23 | run: pip install sphinx breathe exhale git+https://github.com/bashtage/sphinx-material.git 24 | 25 | - name: build sphinx docs 26 | working-directory: docs/Sphinx 27 | run: make html 28 | 29 | - name: Upload artifact 30 | uses: actions/upload-pages-artifact@v3 31 | with: 32 | path: docs/Sphinx/build/html 33 | 34 | deploy: 35 | # Add a dependency to the build job 36 | needs: build 37 | 38 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 39 | permissions: 40 | pages: write # to deploy to Pages 41 | id-token: write # to verify the deployment originates from an appropriate source 42 | 43 | # Deploy to the github-pages environment 44 | environment: 45 | name: github-pages 46 | url: ${{ steps.deployment.outputs.page_url }} 47 | 48 | # Specify runner + deployment step 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Deploy to GitHub Pages 52 | id: deployment 53 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directory 2 | build 3 | 4 | # Resources 5 | resources 6 | 7 | # Docs dirs 8 | docs/Doxygen/gen_docs 9 | docs/Sphinx/source/api 10 | 11 | # VS Code config 12 | .vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixjulianheitmann/licensecxx/f8f1f6b00697c28363fb9f0e4fa39c396f339f8b/.gitmodules -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18 FATAL_ERROR) 2 | 3 | SET(CMAKE_DISABLE_SOURCE_CHANGES OFF) #keys are generated in the source tree by default 4 | SET(CMAKE_DISABLE_IN_SOURCE_BUILD ON) 5 | 6 | option(LCXX_GENERATE_KEYS "Whether RSA key files should be generated during build time." OFF) 7 | option(LCXX_NLOHMANN_JSON_CMAKE_FETCH "Whether licensecxx tries to download its own version of nlohmann::json" ON) 8 | 9 | include(cmake/get_version.cmake) 10 | pull_version() 11 | 12 | project (lcxx 13 | VERSION ${GIT_VER_BASE} 14 | LANGUAGES CXX) 15 | 16 | set(VERSION_DIR ${CMAKE_BINARY_DIR}/version) 17 | configure_file("scripts/version.hpp.in" "${CMAKE_BINARY_DIR}/version/lcxx/version.hpp") 18 | 19 | include(${PROJECT_SOURCE_DIR}/cmake/key_targets.cmake) 20 | 21 | if(LCXX_NLOHMANN_JSON_CMAKE_FETCH) 22 | cmake_policy(SET CMP0135 NEW) 23 | include(${PROJECT_SOURCE_DIR}/cmake/get_nlohmann_json.cmake) 24 | endif() 25 | 26 | add_subdirectory(${PROJECT_SOURCE_DIR}/modules) 27 | 28 | add_subdirectory(${PROJECT_SOURCE_DIR}/tests) 29 | add_subdirectory(${PROJECT_SOURCE_DIR}/samples) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Begin license text. 2 | Copyright 2022 Felix Heitmann 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | End license text. 11 | 12 | This software depends on the following projects with their respective licenses: 13 | - [nlohmann_json](https://github.com/nlohmann/json): MIT License 14 | - [openssl](https://www.openssl.org/): [dual OpenSSL and SSLeay license](https://www.openssl.org/source/license-openssl-ssleay.txt) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Licensecxx 2 | 3 | ![Build & Test Status Ubuntu](https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-ubuntu.yml/badge.svg) 4 | 5 | ![Build & Test Status Windows](https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-windows.yml/badge.svg) 6 | 7 | ![Build & Test Status Mac](https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-mac.yml/badge.svg) 8 | 9 | 10 | - [Licensecxx](#licensecxx) 11 | - [How to use licensecxx](#how-to-use-licensecxx) 12 | - [Prerequisites](#prerequisites) 13 | - [Building](#building) 14 | - [Generate a license](#generate-a-license) 15 | - [Verify a license](#verify-a-license) 16 | - [Generating a key pair](#generating-a-key-pair) 17 | - [License Features (and roadmap)](#license-features-and-roadmap) 18 | - [License](#license) 19 | 20 | *Copy protection library targeting Linux, Windows and Mac* (currently only Linux supported) 21 | 22 | This project is inspired by the [**licensecc**](https://github.com/open-license-manager/licensecc) project and similar to [hyperboloide/lk](https://github.com/hyperboloide/lk). 23 | 24 | Protect your software by generating and checking against license files. Create a set of key value pairs as content for your file and sign them with a RSA private key. The licensed software can then read this license file and verify the signature with the respective public key. 25 | 26 | Additionally to providing key value pairs, you can include hash values of machine or user dependant data. Features such as name of the operating system, the machine hostname, the maximum CPU frequency can be hashed and included in the license. This enables machine/user specific license distribution. 27 | 28 | Licensecxx provides a modern C++ interface with the main focus on ease of use and minimal impact on the software that it's protecting. 29 | 30 | A list of the available features and the roadmap can be seen at [License Features](#license-features). 31 | 32 | This repository is still under development. If you have experience errors or bugs, please file an issue on the GitHub issues page. If you want to contribute, pull requests are very welcome. 33 | 34 | ## How to use licensecxx 35 | 36 | ### Prerequisites 37 | 38 | The library requires C++20 support. 39 | 40 | Licensecxx relies on [nlohmann/json](https://github.com/nlohmann/json) and [OpenSSL](https://www.openssl.org/). 41 | nlohmann/json is fetched through CMake-Fetchcontent during configure time and made available the library. Whether this is the preferrable way is still up to debate. 42 | 43 | OpenSSL needs to be made available by the parent project/user. Licensecxx only calls `FindPackage(OpenSSL)`. If that cannot find the OpenSSL >3.0 library, the licensecxx cannot be built. 44 | 45 | ### Building 46 | 47 | This project is supposed to be used as a library addition to your software. Licensecxx is CMake based and can be included as a submodule in your project. Including it as a subdirectory makes the licensecxx targets available to your project 48 | 49 | ```cmake 50 | add_subdirectory(lcxx) 51 | 52 | target_link_libraries(your-executable PUBLIC 53 | lcxx::lcxx 54 | # lcxx::identifiers 55 | ) 56 | ``` 57 | 58 | The `lcxx::identifiers` library is optional to produce/verify identifying hash codes of the target machine. 59 | 60 | ### Generate a license 61 | 62 | A simple license generator looks like this: 63 | ```c++ 64 | #include 65 | 66 | int main() 67 | { 68 | lcxx::license license; 69 | 70 | // Push optional data into the license file 71 | license.push_content( "some key", "some value" ); 72 | license.push_content( "hardware", lcxx::identifiers::hardware().hash ); 73 | license.push_content( "os", lcxx::identifiers::os().hash ); 74 | 75 | auto key = lcxx::crypto::load_key( some_pem_private_key_rsa_string, lcxx::crypto::key_type::private_key ); 76 | // alternatively, load key from file by providing the PEM file path 77 | // auto key = lcxx::crypto::load_key( some_pem_private_key_rsa_path, lcxx::crypto::key_type::private_key ); 78 | lcxx::to_json(license, "my-project-license.lic", key); 79 | } 80 | ``` 81 | 82 | It prints a signed license json file to the file `my-project-license.lic`. This is similar to the sample [license_generator](samples/license_generator/main.cpp). 83 | 84 | ### Verify a license 85 | 86 | A generated license file or string can be loaded and verified provided a public key. Additional hardware identifiers can be checked against the currently running hardware. Note, that the identifiers are still experimental as they are not supported by all platforms, yet. 87 | 88 | ```c++ 89 | int main() 90 | { 91 | auto key = lcxx::crypto::load_key( some_pem_pem_key_rsa_string, lcxx::crypto::key_type::public_key ); 92 | auto [license, signature] = lcxx::from_json( std::filesystem::path("my-project-license.lic") ); 93 | 94 | if ( !lcxx::verify_license( license, signature, key ) ) { 95 | std::cout << "This program is not licensed!" << std::endl; 96 | return -1; 97 | } 98 | 99 | if ( lcxx::identifiers::verify( hw_ident_strat::all, license.get( "hardware" ) ) ) { 100 | std::cout << "The hardware does not match the one licensed!" << std::endl; 101 | return -2; 102 | } 103 | 104 | return 0; 105 | } 106 | ``` 107 | 108 | A similar sample is given in [license_verifier](samples/license_verifier/main.cpp) 109 | 110 | Further samples are given in the [samples](samples) folder. 111 | 112 | ### Generating a key pair 113 | 114 | To generate an RSA key pair that can be processed by licensecxx the following openssl commands can be used: 115 | ``` 116 | openssl genrsa -out /path/to/private_key.rsa 1024 117 | openssl rsa -in /path/to/private_key.rsa -outform PEM -pubout -out /path/to/public_key 118 | ``` 119 | 120 | These commands generate a private-public key pair. Enabling the CMake option `LCXX_GENERATE_KEYS` will add an additional CMake interface library called `lcxx::key`. 121 | Linking it will have CMake invoke a [simple Python script](scripts/generate_headers.py) that generates a key-pair in two header files: 122 | - `public_key.hpp` 123 | - `private_key.hpp` 124 | 125 | For that, location of the generated private/public keys can be specified with the variables `LCXX_PRIVATE_KEY`/`LCXX_PUBLIC_KEY`. The key size defaults to 1024 but can be configured through `LCXX_KEY_SIZE`. 126 | 127 | Finally, `LCXX_KEY_HEADER_DIR` defines the path where the generated header files should be located. It will automatically be part of the `lcxx::key` include directories. 128 | 129 | Enabling `LCXX_GENERATE_KEYS` will require Python as a dependency for the header generation. 130 | 131 | ## License Features (and roadmap) 132 | 133 | Building and testing on machines other than Linux is not as easy. That's why all features are most stable on Linux. Whenever a bug occurs, I will try to catch it with another unit test. 134 | 135 | The roadmap for what I still have in mind with this library is as follows: 136 | 137 | - [x] include CPU features 138 | - [x] include OS/user features 139 | - [x] Provide proper online documentation 140 | - [x] Make CD/CI run through on Windows / Mac / Ubuntu 141 | - [ ] provide hardware/os identifiers for Windows 142 | - [ ] provide hardware/os identifiers for Mac 143 | - [ ] Provide a conan binary 144 | - [ ] Provide a vcpkg binary 145 | - [ ] Provide CMake fetchcontent option 146 | - [ ] Find a way to test hardware identifiers in CD/CI 147 | 148 | 149 | ## License 150 | The project is donated to the community. It uses the [MIT license](LICENSE). 151 | -------------------------------------------------------------------------------- /cmake/get_nlohmann_json.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz) 4 | FetchContent_MakeAvailable(json) 5 | -------------------------------------------------------------------------------- /cmake/get_version.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | function(pull_version) 4 | 5 | execute_process( 6 | COMMAND git describe --tags 7 | WORKING_DIRECTORY "${CMAKE_PROJECT_DIR}" 8 | OUTPUT_VARIABLE GIT_DESCRIBE 9 | RESULTS_VARIABLE GIT_DESCRIBE_ERR 10 | OUTPUT_STRIP_TRAILING_WHITESPACE 11 | ) 12 | 13 | # Examples: 14 | # - v1.0.3-1-g88da7dd 15 | # - v2.0.0 16 | # - v123.123.321-alpha.543+14-g88da7dd 17 | string(REGEX MATCH "v.*\..*\..*" SEMVER_FOUND ${GIT_DESCRIBE}) 18 | 19 | if("${SEMVER_FOUND}" STREQUAL "") 20 | set(GIT_VER_SEM "v0.0.0-dev") 21 | set(GIT_VER_STR "0.0.0-dev") 22 | set(GIT_VER_MAJOR 0) 23 | set(GIT_VER_MINOR 0) 24 | set(GIT_VER_BUILD 0) 25 | set(GIT_VER_TAIL "dev") 26 | endif() 27 | 28 | string(REGEX MATCHALL "(\\v|\\.)[0-9+]|\\-.*" VER_NUMBERS ${GIT_DESCRIBE}) 29 | 30 | list(LENGTH VER_NUMBERS VER_NUMBERS_LEN) 31 | list(GET VER_NUMBERS 0 GIT_VER_MAJOR) 32 | list(GET VER_NUMBERS 1 GIT_VER_MINOR) 33 | list(GET VER_NUMBERS 2 GIT_VER_BUILD) 34 | string(REGEX REPLACE "\\v([0-9+])" "\\1" GIT_VER_MAJOR ${GIT_VER_MAJOR}) 35 | string(REGEX REPLACE "\\.([0-9+])" "\\1" GIT_VER_MINOR ${GIT_VER_MINOR}) 36 | string(REGEX REPLACE "\\.([0-9+])" "\\1" GIT_VER_BUILD ${GIT_VER_BUILD}) 37 | 38 | if(VER_NUMBERS_LEN GREATER_EQUAL 4) 39 | list(GET VER_NUMBERS 3 GIT_VER_TAIL) 40 | # Remove hash at the end 41 | string(REGEX REPLACE "\\-[A-z0-9]*$" "" GIT_VER_TAIL ${GIT_VER_TAIL}) 42 | if(NOT "${GIT_VER_TAIL}" STREQUAL "") 43 | # Remove the - at the beginning 44 | string(REGEX REPLACE "^\\-(.*)" "\\1" GIT_VER_TAIL ${GIT_VER_TAIL}) 45 | # Replace -BUILD_NO with +BUILD_NO 46 | string(REGEX REPLACE "(\\-)([0-9]*)$" "+\\2" GIT_VER_TAIL ${GIT_VER_TAIL}) 47 | endif() 48 | else() 49 | set(GIT_VER_TAIL "") 50 | endif() 51 | 52 | string(JOIN "." GIT_VER_BASE ${GIT_VER_MAJOR} ${GIT_VER_MINOR} ${GIT_VER_BUILD}) 53 | string(JOIN "-" GIT_VER_STR ${GIT_VER_BASE} ${GIT_VER_TAIL}) 54 | string(JOIN "" GIT_VER_SEM "v" ${GIT_VER_STR}) 55 | message("Git describe: ${GIT_DESCRIBE}") 56 | message("GIT_VER_SEM: ${GIT_VER_SEM}") 57 | message("GIT_VER_STR: ${GIT_VER_STR}") 58 | message("GIT_VER_MAJOR: ${GIT_VER_MAJOR}") 59 | message("GIT_VER_MINOR: ${GIT_VER_MINOR}") 60 | message("GIT_VER_BUILD: ${GIT_VER_BUILD}") 61 | message("GIT_VER_TAIL: ${GIT_VER_TAIL}") 62 | 63 | set(GIT_VER_SEM ${GIT_VER_SEM} PARENT_SCOPE) 64 | set(GIT_VER_STR ${GIT_VER_STR} PARENT_SCOPE) 65 | set(GIT_VER_BASE ${GIT_VER_BASE} PARENT_SCOPE) 66 | set(GIT_VER_MAJOR ${GIT_VER_MAJOR} PARENT_SCOPE) 67 | set(GIT_VER_MINOR ${GIT_VER_MINOR} PARENT_SCOPE) 68 | set(GIT_VER_BUILD ${GIT_VER_BUILD} PARENT_SCOPE) 69 | set(GIT_VER_TAIL ${GIT_VER_TAIL} PARENT_SCOPE) 70 | 71 | endfunction() -------------------------------------------------------------------------------- /cmake/key_targets.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Optionally have the key files be generated during build time if NOT already present 3 | if(LCXX_GENERATE_KEYS) 4 | 5 | IF(NOT LCXX_KEY_SIZE) 6 | set(LCXX_KEY_SIZE 2048) 7 | ENDIF() 8 | 9 | if(NOT LCXX_PRIVATE_KEY) 10 | set(LCXX_PRIVATE_KEY "${CMAKE_SOURCE_DIR}/resources/project_keys/private_key.rsa" ) 11 | endif(NOT LCXX_PRIVATE_KEY) 12 | 13 | if(NOT LCXX_PUBLIC_KEY) 14 | set(LCXX_PUBLIC_KEY "${CMAKE_SOURCE_DIR}/resources/project_keys/public_key" ) 15 | endif(NOT LCXX_PUBLIC_KEY) 16 | 17 | if(NOT LCXX_KEY_HEADER_DIR) 18 | set(LCXX_KEY_HEADER_DIR "${PROJECT_BINARY_DIR}/keys/include" ) 19 | endif(NOT LCXX_KEY_HEADER_DIR) 20 | 21 | find_package(Python REQUIRED) 22 | 23 | add_custom_target(generate_headers 24 | COMMAND "${Python_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/generate_headers.py" "${LCXX_PRIVATE_KEY}" "${LCXX_PUBLIC_KEY}" ${LCXX_KEY_HEADER_DIR} 25 | ) 26 | 27 | add_library(lcxx_key_headers INTERFACE) 28 | target_include_directories(lcxx_key_headers INTERFACE 29 | ${LCXX_KEY_HEADER_DIR} 30 | ) 31 | add_dependencies(lcxx_key_headers generate_headers) 32 | add_library(lcxx::key ALIAS lcxx_key_headers) 33 | 34 | if(NOT EXISTS ${LCXX_PRIVATE_KEY}) 35 | cmake_path(GET LCXX_PRIVATE_KEY PARENT_PATH LCXX_PRIVATE_KEY_DIR) 36 | file(MAKE_DIRECTORY ${LCXX_PRIVATE_KEY_DIR}) 37 | 38 | add_custom_target(generate_private_key 39 | COMMAND "openssl" "genrsa" "-out" "${LCXX_PRIVATE_KEY}" "${LCXX_KEY_SIZE}" 40 | ) 41 | add_dependencies(generate_headers generate_private_key) 42 | endif() 43 | 44 | if(NOT EXISTS ${LCXX_PUBLIC_KEY}) 45 | cmake_path(GET LCXX_PUBLIC_KEY PARENT_PATH LCXX_PUBLIC_KEY_DIR) 46 | file(MAKE_DIRECTORY ${LCXX_PUBLIC_KEY_DIR}) 47 | 48 | add_custom_target(generate_public_key 49 | COMMAND "openssl" "rsa" "-in" "${LCXX_PRIVATE_KEY}" "-outform" "PEM" "-pubout" "-out" "${LCXX_PUBLIC_KEY}" 50 | ) 51 | add_dependencies(generate_headers generate_public_key) 52 | endif() 53 | 54 | endif(LCXX_GENERATE_KEYS) 55 | -------------------------------------------------------------------------------- /docs/Doxygen/Doxyfile-prj.cfg: -------------------------------------------------------------------------------- 1 | @INCLUDE = "./Doxyfile.cfg" 2 | GENERATE_HTML = NO 3 | GENERATE_XML = YES 4 | XML_PROGRAMLISTING = NO 5 | 6 | # Project Stuff 7 | PROJECT_NAME = "Licensecxx" 8 | PROJECT_BRIEF = "Software licensing, copy protection in C++" 9 | OUTPUT_DIRECTORY = "./gen_docs" 10 | 11 | # Inputs 12 | INPUT = "./../../modules" 13 | RECURSIVE = YES 14 | 15 | # Reduce Enum type indexing 16 | MAX_INITIALIZER_LINES = 0 -------------------------------------------------------------------------------- /docs/Doxygen/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "licensecxx" 3 | version = "0.1.0" 4 | description = "Software licensing, copy protection in C++" 5 | authors = ["Felix "] 6 | license = "MIT License" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | breathe = "^4.34.0" 12 | exhale = "^0.3.5" 13 | 14 | 15 | [build-system] 16 | requires = ["poetry-core"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /docs/Sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/Sphinx/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/Sphinx/source/How-to-use-it.rst: -------------------------------------------------------------------------------- 1 | How to use licensecxx 2 | ===================== 3 | 4 | 5 | Prerequisites 6 | ------------- 7 | 8 | Licensecxx relies on nlohmann-json_ and OpenSSL_. 9 | nlohmann/json is fetched through CMake-Fetchcontent during configure time and made available the library. Whether this is the preferrable way is still up to debate. 10 | 11 | OpenSSL needs to be made available by the parent project/user. Licensecxx only calls ``FindPackage(OpenSSL)``. If that cannot find the OpenSSL 1.1 library, the licensecxx cannot be built. 12 | 13 | Building 14 | -------- 15 | 16 | This project is supposed to be used as a library addition to your software. Licensecxx is CMake based and can be included as a submodule in your project. Including it as a subdirectory makes the licensecxx targets available to your project 17 | 18 | .. code-block::cmake 19 | add_subdirectory(lcxx) 20 | 21 | target_link_libraries(your-executable PUBLIC 22 | lcxx::lcxx 23 | # lcxx::identifiers 24 | ) 25 | 26 | The ``lcxx::identifiers`` library is optional to produce/verify identifying hash codes of the target machine. 27 | 28 | Note that `LCXX_NLOHMANN_JSON_CMAKE_FETCH` can be explicitly turned off to prevent LCXX from downloading Nlohmann::Json using CMake Fetchcontent. 29 | 30 | Generate a license 31 | ------------------ 32 | 33 | A simple license generator looks like this: 34 | 35 | .. code-block::c++ 36 | #include 37 | 38 | int main() 39 | { 40 | lcxx::license license; 41 | 42 | // Push optional data into the license file 43 | license.push_content( "some key", "some value" ); 44 | license.push_content( "hardware", lcxx::identifiers::hardware().hash ); 45 | license.push_content( "os", lcxx::identifiers::os().hash ); 46 | 47 | auto key = lcxx::crypto::load_key( some_pem_private_key_rsa_string, lcxx::crypto::key_type::private_key ); 48 | // alternatively, load key from file by providing the file PEM file path 49 | // auto key = lcxx::crypto::load_key( some_pem_private_key_rsa_path, lcxx::crypto::key_type::private_key ); 50 | lcxx::to_json(license, "my-project-license.lic", key); 51 | } 52 | 53 | 54 | It prints a signed license json file to the file ``my-project-license.lic``. This is similar to the sample [license_generator](samples/license_generator/main.cpp). 55 | 56 | Verify a license 57 | ---------------- 58 | 59 | A generated license file or string can be loaded and verified provided a public key. Additional hardware identifiers can be checked against the currently running hardware. 60 | 61 | .. code-block:: c++ 62 | 63 | int main() 64 | { 65 | auto key = lcxx::crypto::load_key( some_pem_pem_key_rsa_string, lcxx::crypto::key_type::public_key ); 66 | auto [license, signature] = lcxx::from_json( std::filesystem::path("my-project-license.lic") ); 67 | 68 | if ( !lcxx::verify_license( license, signature, key ) ) { 69 | std::cout << "This program is not licensed!" << std::endl; 70 | return -1; 71 | } 72 | 73 | if ( lcxx::identifiers::verify( license.get( "hardware" ) ) ) { 74 | std::cout << "The hardware does not match the one licensed!" << std::endl; 75 | return -2; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | A similar sample is given in license_verifier_ 82 | 83 | Further samples are given in the samples_ folder. 84 | 85 | Generating a key pair 86 | --------------------- 87 | 88 | To generate an RSA key pair that can be processed by licensecxx the following openssl commands can be used: 89 | 90 | .. code-block:: 91 | openssl genrsa -out /path/to/private_key.rsa 1024 92 | openssl rsa -in /path/to/private_key.rsa -outform PEM -pubout -out /path/to/public_key 93 | 94 | 95 | These command generate a private-public key pair. Enabling the CMake option ``LCXX_GENERATE_KEYS`` will an additional CMake interface library called ``lcxx::key``. 96 | Linking it will have CMake generate a key-pair from that through a simple python script two header files: 97 | - ``public_key.hpp`` 98 | - ``private_key.hpp`` 99 | 100 | For that location of the generated private/public keys can be specified with the variables ``LCXX_PRIVATE_KEY`` / ``LCXX_PUBLIC_KEY``. The key size defaults to 1024 but can be configured through ``LCXX_KEY_SIZE``. 101 | 102 | Finally, ``LCXX_KEY_HEADER_DIR`` defines the path where the generated header files should be located. It will automatically be part of the ``lcxx::key`` include directories. 103 | 104 | Enabling ``LCXX_GENERATE_KEYS`` will require Python as a dependency for the header generation. 105 | 106 | .. _license_verifier : samples/license_verifier/main.cpp 107 | .. _samples: samples 108 | .. _nlohmann-json: https://github.com/nlohmann/json 109 | .. _OpenSSL: https://www.openssl.org -------------------------------------------------------------------------------- /docs/Sphinx/source/Identifiers.rst: -------------------------------------------------------------------------------- 1 | .. _identifiers_doc: 2 | 3 | Identifiers 4 | =========== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | :caption: Contents: 9 | 10 | `Note, that the identifiers feature is currently in the experimental stage. I don't have a Windows/Mac machine available, regularly. That's why the os dependent features still take time.` 11 | 12 | Licensecxx allows to gather information about the system currently running the software. To use those features, link against the ``lcxx::identifiers`` target in your CMake configuration. 13 | 14 | This will provide the following headers: 15 | - ``lcxx/identifiers/hardware.hpp`` 16 | - ``lcxx/identifiers/os.hpp`` 17 | 18 | There two primary functions are given (arguments omitted): 19 | - ``lcxx::identifiers::()`` (e.g. ``lcxx::identifiers::hardware()``) 20 | - ``lcxx::identifiers::verify()`` 21 | 22 | The type of information that will be collected depends on the chosen strategy. Sticking to the ``hardware`` example, the enum ``hw_ident_strat`` defines which information will be incorporated into the hardware identifier. 23 | 24 | Passing the value ``hw_ident_strat::all`` will try to collect as much info about the machine and generate a single ``identifier`` object from that. This object contains a string containing all collect info and a hash-digest of that string. 25 | 26 | This hash (or the respective string) can be included as a value into the license. 27 | 28 | The ``lcxx::identifiers::verify`` function takes a ``hw_ident_strat`` and a hash value and will check whether those match, given the current underlying hardware. 29 | 30 | Multiple identification strategies can be combined by using the ``operator|``. Using ``hw_ident_strat::cpu_n_cores | hw_ident_strat::cpu_model_name`` will only collect the number of 31 | available CPU cores and the CPU model name. 32 | 33 | The following code snipped would take a license object and push in the hardware identifier consisting of no. of CPU cores and its model name. 34 | 35 | .. code-block:: c++ 36 | 37 | #include 38 | #include 39 | 40 | int main() 41 | { 42 | using namespace lcxx; 43 | using hw_strat = identifiers::hw_ident_strat; 44 | 45 | license lic; 46 | auto [hash, text] = identifiers::hardware( hw_strat::cpu_n_cores | hw_strat::cpu_model_name ); 47 | lic.push_content( "hardware", hash ); 48 | 49 | // ... rest of the software 50 | } 51 | 52 | 53 | Hardware 54 | -------- 55 | 56 | The hardware header (``lcxx/identifier/hardware.hpp``) allows to collect information about the systems hardware, if the current user has the privileges to access that information. 57 | 58 | The available strategies are listed by the ``hw_ident_strat`` enum and have the following meaning 59 | 60 | ================= ================================================================================================================== 61 | Enumerator Meaning 62 | ================= ================================================================================================================== 63 | all All available information is gathered. Any other additionally set strategy is ignored. 64 | cpu All information regarding the builtin CPU is gathered. Any other additionally, set CPU strategy is ignored. 65 | cpu_n_cores Try to collect the number physical CPU cores. 66 | cpu_n_threads Try to detect the number of available hardware threads. 67 | cpu_max_frequency Try to discover the maximum frequency in Hz of the builtin CPU. 68 | cpu_vendor Try to discover the vendor name of the CPU. 69 | cpu_model_name Try to discover the CPU's model name. 70 | ================= ================================================================================================================== 71 | 72 | Note, that multi-CPU setups are not supported. Such a setup may yield unexpected values for CPU related info. 73 | 74 | Operating System 75 | ---------------- 76 | 77 | The OS header (``lcxx/identifier/os.hpp``) allows to collect information about the operating system and the current user, if the current user has the privileges to access that information. 78 | 79 | The available strategies are listed by the ``os_ident_strat`` enum and have the following meaning 80 | 81 | ================= ================================================================================================================== 82 | Enumerator Meaning 83 | ================= ================================================================================================================== 84 | all All available information is gathered. Any other additionally set strategy is ignored. 85 | os All available information regarding the OS is gathered. Any other additionally set OS strategies are ignored. 86 | user All available information regarding the user is gathered. Any other additionally set user strategies are ignored. 87 | os_name Try to discover the OS name (e.g. ``Linux``) 88 | os_architecture Try to discover the OS architecture 89 | os_pc_name Try to discover the OS PC name (or hostname) 90 | user_name Try to discover the currently logged in user's name 91 | user_uid Try to discover the currently logged in user's id 92 | user_groups Try to discover the groups the currently logged in user belongs to 93 | user_gids Try to discover the group ids the currently logged in user belongs to 94 | ================= ================================================================================================================== 95 | -------------------------------------------------------------------------------- /docs/Sphinx/source/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixjulianheitmann/licensecxx/f8f1f6b00697c28363fb9f0e4fa39c396f339f8b/docs/Sphinx/source/_static/.gitkeep -------------------------------------------------------------------------------- /docs/Sphinx/source/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixjulianheitmann/licensecxx/f8f1f6b00697c28363fb9f0e4fa39c396f339f8b/docs/Sphinx/source/_templates/.gitkeep -------------------------------------------------------------------------------- /docs/Sphinx/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | project = 'Licensecxx' 10 | copyright = '2022, Felix Heitmann' 11 | author = 'Felix Heitmann' 12 | release = 'latest' 13 | 14 | # -- General configuration --------------------------------------------------- 15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 16 | 17 | extensions = [ 18 | 'breathe', 19 | 'exhale' 20 | ] 21 | 22 | templates_path = ['_templates'] 23 | exclude_patterns = [] 24 | 25 | language = 'en' 26 | 27 | # -- Options for HTML output ------------------------------------------------- 28 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 29 | 30 | html_theme = 'alabaster' 31 | html_static_path = ['_static'] 32 | 33 | # -- Exhale configuration -------------------------------------------------- 34 | breathe_projects = { 35 | "Licensecxx": "./../../Doxygen/gen_docs/xml" 36 | } 37 | breathe_default_project = "Licensecxx" 38 | 39 | exhale_args = { 40 | "containmentFolder": "./api", 41 | "rootFileName": "library_root.rst", 42 | "rootFileTitle": "Library API", 43 | "doxygenStripFromPath": "..", 44 | "createTreeView": True, 45 | } 46 | 47 | primary_domain = 'cpp' 48 | highlight_language = 'cpp' 49 | 50 | # -- Theme configuration -------------------------------------------------- 51 | 52 | html_theme = 'sphinx_material' 53 | 54 | html_sidebars = { 55 | "**": ["globaltoc.html"] 56 | } 57 | 58 | html_theme_options = { 59 | 'nav_title': 'Licensecxx', 60 | 'nav_links': [ 61 | { 62 | 'href': 'Intro', 63 | 'title': 'Intro', 64 | 'internal': True, 65 | }, 66 | { 67 | 'href': 'Identifiers', 68 | 'title': 'Identifiers', 69 | 'internal': True, 70 | }, 71 | { 72 | 'href': 'How-to-use-it', 73 | 'title': 'How to use licensecxx', 74 | 'internal': True, 75 | }, 76 | ], 77 | 78 | # 'version_dropdown': True, 79 | # 'version_info': { 80 | # 'Release': '.', 81 | # '0.1.0': '0.1.0', 82 | # }, 83 | 84 | # Specify a base_url used to generate sitemap.xml. If not 85 | # specified, then no sitemap will be built. 86 | 'base_url': 'https://felixjulianheitmann.github.io/licensecxx', 87 | 88 | # Set the color and the accent color 89 | 'color_primary': 'blue', 90 | 'color_accent': 'light-blue', 91 | 92 | # Set the repo location to get a badge with stats 93 | 'repo_url': 'https://github.com/felixjulianheitmann/licensecxx/', 94 | 'repo_name': 'Licensecxx', 95 | 96 | # Visible levels of the global TOC; -1 means unlimited 97 | 'globaltoc_depth': 3, 98 | # If False, expand all TOC entries 99 | 'globaltoc_collapse': False, 100 | # If True, show hidden TOC entries 101 | 'globaltoc_includehidden': False, 102 | } 103 | -------------------------------------------------------------------------------- /docs/Sphinx/source/index.rst: -------------------------------------------------------------------------------- 1 | .. Licensecxx documentation master file, created by 2 | sphinx-quickstart on Wed Sep 21 12:41:19 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Licensecxx's documentation! 7 | ====================================== 8 | 9 | .. |Build & Test Status Ubuntu| image:: https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-ubuntu.yml/badge.svg 10 | 11 | .. |Build & Test Status Windows| image:: https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-windows.yml/badge.svg 12 | 13 | .. |Build & Test Status Mac| image:: https://github.com/felixjulianheitmann/licensecxx/actions/workflows/build-test-mac.yml/badge.svg 14 | 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | :caption: Contents: 19 | 20 | Identifiers 21 | How to use licensecxx 22 | api/library_root 23 | 24 | Intro 25 | ----- 26 | 27 | *Copy protection library targeting Linux, Windows and Mac* (currently only Linux supported) 28 | 29 | This project is inspired by the licensecc_ project. 30 | 31 | Protect your software by generating and checking against license files. Create a set of key value pairs as content for your file and sign them with a RSA private key. The licensed software can then read this license file and verify the signature with the respective public key. 32 | 33 | Additionally to providing key value pairs, you can include hash values of machine or user dependant data. Features such as name of the operating system, the machine hostname, the maximum CPU frequency can be hashed and included in the license. This enables machine/user specific license distribution. 34 | 35 | Licensecxx provides a modern C++ interface with the main focus on ease of use and minimal impact on the software that it's protecting. 36 | 37 | Due to the early development stage, licensecxx currently only supports linux hosts. Most of the software should be os-agnostic, but hardware/os identifiers rely on the operatings system they run on. One of the primary goals will be to enable cross-platform support, as soon as I can get my hands on a Windows/Mac to test things on. 38 | 39 | A list of the collectable environment information is given at :ref:`identifiers_doc` 40 | 41 | This repository is still under development. If you have experience errors or bugs, please file an issue on the GitHub issues page. If you want to contribute, pull requests are very welcome. 42 | 43 | 44 | Why use it 45 | ---------- 46 | 47 | This library makes it easy to generate any form of license protection for your software. 48 | 49 | You can freely include any form of verifyable information, that will be signed and limit the use of your software. 50 | 51 | When I needed this feature myself, I could not find any open source tools that would help me with it, except for licensecc_. 52 | The problem I had with their toolchain was that it requires the user project to be tailored around the licensecc project. 53 | I would have much rather preferred a simple library that allows me to generate signatures through a private keys and verify these with a corresponding public key. 54 | 55 | This is the idea that spawned licensecxx: Provide a modern C++ library interface, that allows signing and verifying key-value pairs, 56 | which is easy to use and has minimal impact on the including user project. 57 | 58 | Similarly, to it's spiritual predecessor, it provides the option to collect system information such as CPU-Name, OS-Name, Hostname, etc, hash them, 59 | and incorporate them into the license. A full list of collectable info can be found at :ref:`identifiers_doc`. 60 | 61 | 62 | 63 | Indices and tables 64 | ------------------ 65 | 66 | * :ref:`genindex` 67 | * :ref:`modindex` 68 | * :ref:`search` 69 | 70 | .. _licensecc: https://github.com/open-license-manager/licensecc 71 | -------------------------------------------------------------------------------- /docs/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "licensecxx" 3 | version = "0.1.0" 4 | description = "Software licensing, copy protection in C++" 5 | authors = ["Felix "] 6 | license = "MIT License" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | exhale = "^0.3.5" 12 | breathe = "^4.34.0" 13 | 14 | 15 | [build-system] 16 | requires = ["poetry-core"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(crypto) 2 | add_subdirectory(identifiers) 3 | add_subdirectory(ident_utils) 4 | add_subdirectory(lcxx) 5 | add_subdirectory(license) -------------------------------------------------------------------------------- /modules/crypto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lcxx_crypto 2 | ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/src/encoding.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/src/hash.cpp 5 | ) 6 | 7 | find_package(OpenSSL COMPONENTS Crypto REQUIRED) 8 | 9 | target_compile_features(lcxx_crypto PUBLIC cxx_std_20) 10 | set_target_properties(lcxx_crypto PROPERTIES CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | target_link_libraries( lcxx_crypto PUBLIC 13 | OpenSSL::Crypto 14 | ) 15 | 16 | target_include_directories(lcxx_crypto PUBLIC 17 | ${CMAKE_CURRENT_SOURCE_DIR}/include 18 | ${VERSION_DIR} 19 | ) 20 | 21 | add_library(lcxx::crypto ALIAS lcxx_crypto) -------------------------------------------------------------------------------- /modules/crypto/include/lcxx/crypto.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__CRYPTO_CRYPTO_HPP__ 2 | #define LCXX__CRYPTO_CRYPTO_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace lcxx::crypto { 12 | 13 | using rsa_key_t = std::shared_ptr< EVP_PKEY >; 14 | 15 | enum class key_type { 16 | public_key, 17 | private_key, 18 | }; 19 | 20 | /** 21 | * @brief loads a key (public/private) from string into memory. 22 | * 23 | * @param key the RSA key data in PEM format 24 | * @param type whether this is a public or private key 25 | */ 26 | auto load_key( std::string const & key, key_type type ) -> rsa_key_t; 27 | 28 | /** 29 | * @brief loads a key (public/private) from file into memory. File must be PEM type 30 | * 31 | * @param key_path file path to the corresponding RSA key file in PEM format 32 | * @param type whether this is a public or private key 33 | */ 34 | auto load_key( std::filesystem::path const & key_path, key_type type ) -> rsa_key_t; 35 | 36 | /** 37 | * @brief this method will sign an input string and a private RSA key and return the encrypted data 38 | * 39 | * @param input_string the string that should be signed 40 | * @param private_key the private RSA key used for the signature 41 | * @return std::string the encrypted string 42 | */ 43 | auto sign( std::string const & input_string, rsa_key_t const private_key ) -> std::vector< std::byte >; 44 | 45 | /** 46 | * @brief takes a reference string, a corresponding signature and a public key to verify if the signature matches 47 | * that reference string 48 | * 49 | * @param reference the input data that was used to create the signature 50 | * @param signature the signature that is checked against the reference data 51 | * @param public_key the public RSA key corresponding to the one used for the signature 52 | * @return true if the signature matches the reference string, given the loaded public key 53 | * @return false if signature and reference string do not match, given the loaded public key 54 | */ 55 | auto verify_signature( std::string_view const reference, std::vector< std::byte > const & signature, 56 | rsa_key_t const public_key ) -> bool; 57 | 58 | } // namespace lcxx::crypto 59 | 60 | #endif // LCXX__CRYPTO_CRYPTO_HPP__ -------------------------------------------------------------------------------- /modules/crypto/include/lcxx/encoding.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__CRYPTO_ENCODING_HPP__ 2 | #define LCXX__CRYPTO_ENCODING_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lcxx::encode { 10 | 11 | /** 12 | * @brief encodes a byte span to a base64 string 13 | * 14 | * @param bytes the byte span that will be encoded 15 | * @return std::string the input bytes as base64 encoded string 16 | */ 17 | auto base64( std::span< const std::byte > const bytes ) -> std::string; 18 | 19 | template < typename T > 20 | requires( sizeof( T ) == 1 ) auto base64( std::span< const T > const bytes ) -> std::string 21 | { 22 | return base64( { reinterpret_cast< std::byte const * >( bytes.data() ), bytes.size() } ); 23 | } 24 | 25 | } // namespace lcxx::encode 26 | 27 | namespace lcxx::decode { 28 | 29 | /** 30 | * @brief takes a base64 encoded string and returns the contained bytes as a vector 31 | * 32 | * @param input a string representing base64 encoded data 33 | * @return std::vector< std::byte > the input data in byte form 34 | */ 35 | auto base64( std::string const & input ) -> std::vector< std::byte >; 36 | 37 | } // namespace lcxx::decode 38 | 39 | #endif // LCXX__CRYPTO_ENCODING_HPP__ 40 | -------------------------------------------------------------------------------- /modules/crypto/include/lcxx/hash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__CRYPTO_HASH_HPP__ 2 | #define LCXX__CRYPTO_HASH_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace lcxx::hash { 11 | 12 | enum class error { 13 | ok, 14 | ctx_allocation_failure, 15 | digest_init_failure, 16 | digest_update_failure, 17 | digest_finalization_failure, 18 | }; 19 | 20 | using sha512_hash_t = std::array< std::byte, 64 >; 21 | 22 | /** 23 | * @brief calculates a SHA512 hashsum over an input string 24 | * 25 | * @param input the string that will be digested 26 | * @return sha512_hash_t the SHA512 hash in byte-array form 27 | */ 28 | auto sha512( std::string const & input ) -> std::pair< sha512_hash_t, error >; 29 | auto sha512( std::span< const std::byte > const input ) -> std::pair< sha512_hash_t, error >; 30 | template < typename T > 31 | requires( sizeof( T ) == 1 ) 32 | auto sha512( std::span< T > const input ) -> std::pair< sha512_hash_t, error > 33 | { 34 | return sha512( 35 | std::span< const std::byte >{ reinterpret_cast< std::byte const * >( input.data() ), input.size() } ); 36 | } 37 | 38 | } // namespace lcxx::hash 39 | 40 | #endif // LCXX__CRYPTO_HASH_HPP__ 41 | -------------------------------------------------------------------------------- /modules/crypto/src/crypto.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace lcxx::crypto { 16 | 17 | namespace { 18 | 19 | void context_deleter( EVP_MD_CTX * ctx ) 20 | { 21 | if ( ctx ) 22 | EVP_MD_CTX_destroy( ctx ); 23 | } 24 | 25 | using context = std::unique_ptr< EVP_MD_CTX, decltype( &context_deleter ) >; 26 | 27 | auto create_context() -> context 28 | { 29 | context ctx( EVP_MD_CTX_create(), context_deleter ); 30 | if ( !ctx ) { 31 | throw std::runtime_error( "Could not create OpenSSL context" ); 32 | } 33 | 34 | return ctx; 35 | } 36 | } // namespace 37 | 38 | void bio_deleter( BIO * bio ) 39 | { 40 | if ( bio ) 41 | BIO_free( bio ); 42 | } 43 | 44 | void key_deleter( EVP_PKEY * key ) 45 | { 46 | if ( key ) 47 | EVP_PKEY_free( key ); 48 | }; 49 | 50 | auto load_key( std::filesystem::path const & key_path, key_type const type ) -> rsa_key_t 51 | { 52 | std::ifstream ifs{ key_path.string() }; 53 | if ( ifs ) { 54 | std::string key{ std::istreambuf_iterator< char >{ ifs }, std::istreambuf_iterator< char >{} }; 55 | return load_key( key, type ); 56 | } 57 | else { 58 | throw std::runtime_error( std::string{ "Could not open key file at: " } + key_path.string() ); 59 | } 60 | } 61 | 62 | auto load_key( std::string const & key, key_type const type ) -> rsa_key_t 63 | { 64 | std::unique_ptr< BIO, decltype( &bio_deleter ) > bio( 65 | BIO_new_mem_buf( static_cast< const void * >( key.c_str() ), key.size() ), bio_deleter ); 66 | 67 | rsa_key_t rsa_key = { nullptr, key_deleter }; 68 | 69 | if ( type == key_type::private_key ) { 70 | rsa_key = { PEM_read_bio_PrivateKey( bio.get(), nullptr, nullptr, nullptr ), key_deleter }; 71 | } 72 | else { 73 | rsa_key = { PEM_read_bio_PUBKEY( bio.get(), nullptr, nullptr, nullptr ), key_deleter }; 74 | } 75 | 76 | if ( !rsa_key ) 77 | throw std::logic_error( "Could not properly parse the provided key" ); 78 | return rsa_key; 79 | } 80 | 81 | auto sign( std::string const & input_string, rsa_key_t const private_key ) -> std::vector< std::byte > 82 | { 83 | static_assert( sizeof( unsigned char ) == sizeof( std::byte ) ); 84 | auto ctx = create_context(); 85 | 86 | if ( 1 != EVP_DigestSignInit( ctx.get(), NULL, EVP_sha512(), NULL, private_key.get() ) ) { 87 | throw std::runtime_error( "Could not initialize openSSL digest" ); 88 | } 89 | 90 | if ( 1 != EVP_DigestSignUpdate( ctx.get(), input_string.data(), input_string.size() ) ) { 91 | throw std::runtime_error( "Could not updat the openSSL digest" ); 92 | } 93 | 94 | size_t len; 95 | if ( 1 != EVP_DigestSignFinal( ctx.get(), NULL, &len ) ) { 96 | throw std::runtime_error( "Could not calculate signature length" ); 97 | } 98 | 99 | std::vector< std::byte > signature( len ); 100 | if ( 1 != EVP_DigestSignFinal( ctx.get(), reinterpret_cast< unsigned char * >( signature.data() ), &len ) ) { 101 | throw std::runtime_error( "Could not calculate signature" ); 102 | } 103 | 104 | return signature; 105 | } 106 | 107 | auto verify_signature( std::string_view const reference, std::vector< std::byte > const & signature, 108 | rsa_key_t const public_key ) -> bool 109 | { 110 | 111 | auto ctx = create_context(); 112 | if ( 1 != EVP_DigestVerifyInit( ctx.get(), NULL, EVP_sha512(), NULL, public_key.get() ) ) { 113 | throw std::runtime_error( "could not initialize openSSL digest" ); 114 | } 115 | 116 | if ( 1 != EVP_DigestVerifyUpdate( ctx.get(), reinterpret_cast< void const * >( reference.data() ), 117 | reference.size() ) ) { 118 | throw std::runtime_error( "could not read in openSSL reference data" ); 119 | } 120 | 121 | return ( 1 == EVP_DigestVerifyFinal( ctx.get(), reinterpret_cast< unsigned char const * >( signature.data() ), 122 | signature.size() ) ); 123 | } 124 | 125 | } // namespace lcxx::crypto 126 | -------------------------------------------------------------------------------- /modules/crypto/src/encoding.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace lcxx::encode { 8 | auto base64( std::span< const std::byte > const bytes ) -> std::string 9 | { 10 | // Add additional 4 bytes as buffer, because the rounding would just make it more complicated. 11 | std::vector< unsigned char > encoded( bytes.size() * 4 / 3 + 4 + 1, 0 ); 12 | auto true_size = 13 | EVP_EncodeBlock( encoded.data(), reinterpret_cast< unsigned char const * >( bytes.data() ), bytes.size() ); 14 | 15 | // Do not include terminating null-character 16 | return std::string{ encoded.begin(), encoded.begin() + true_size }; 17 | } 18 | 19 | } // namespace lcxx::encode 20 | 21 | namespace lcxx::decode { 22 | 23 | auto base64( std::string const & input ) -> std::vector< std::byte > 24 | { 25 | 26 | std::vector< std::byte > decoded( input.size() * 3 / 4, std::byte{ 0 } ); 27 | 28 | EVP_DecodeBlock( reinterpret_cast< unsigned char * >( decoded.data() ), 29 | reinterpret_cast< unsigned char const * >( input.data() ), input.size() ); 30 | 31 | // Remove empty bytes at the end 32 | if ( input.size() >= 1 && input.back() == '=' ) 33 | decoded.pop_back(); 34 | if ( input.size() >= 2 && *( input.end() - 2 ) == '=' ) 35 | decoded.pop_back(); 36 | return decoded; 37 | } 38 | 39 | } // namespace lcxx::decode 40 | -------------------------------------------------------------------------------- /modules/crypto/src/hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace { 8 | struct mdctx_free { 9 | void operator()( EVP_MD_CTX * md_ctx ) 10 | { 11 | if ( md_ctx ) 12 | EVP_MD_CTX_free( md_ctx ); 13 | } 14 | }; 15 | } // namespace 16 | 17 | namespace lcxx::hash { 18 | 19 | auto sha512( std::string const & input ) -> std::pair< sha512_hash_t, error > 20 | { 21 | std::span< const std::byte > const byte_span = { reinterpret_cast< std::byte const * >( input.data() ), 22 | input.size() }; 23 | return sha512( byte_span ); 24 | } 25 | 26 | auto sha512( std::span< const std::byte > const input ) -> std::pair< sha512_hash_t, error > 27 | { 28 | sha512_hash_t hash_buffer; 29 | auto with = [&hash_buffer]( error code ) { return std::make_pair( hash_buffer, code ); }; 30 | 31 | auto ctx = std::unique_ptr< EVP_MD_CTX, mdctx_free >( EVP_MD_CTX_new(), mdctx_free{} ); 32 | if ( ctx == nullptr ) 33 | return with( error::ctx_allocation_failure ); 34 | 35 | if ( 1 != EVP_DigestInit_ex( ctx.get(), EVP_sha512(), NULL ) ) 36 | return with( error::digest_init_failure ); 37 | 38 | if ( 1 != EVP_DigestUpdate( ctx.get(), input.data(), input.size() ) ) 39 | return with( error::digest_update_failure ); 40 | 41 | unsigned int len; 42 | if ( 1 != EVP_DigestFinal_ex( ctx.get(), reinterpret_cast< unsigned char * >( hash_buffer.data() ), &len ) ) 43 | return with( error::digest_finalization_failure ); 44 | 45 | return with( error::ok ); 46 | } 47 | 48 | } // namespace lcxx::hash 49 | -------------------------------------------------------------------------------- /modules/ident_utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lcxx_ident_utils) 2 | 3 | target_include_directories(lcxx_ident_utils PUBLIC 4 | ${CMAKE_CURRENT_SOURCE_DIR}/include 5 | ${VERSION_DIR} 6 | ) 7 | 8 | target_compile_features(lcxx_ident_utils PUBLIC cxx_std_20) 9 | set_target_properties(lcxx_ident_utils PROPERTIES CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | if(WIN32) 12 | message("Configuring OS utilities for Windows") 13 | target_sources(lcxx_ident_utils PUBLIC 14 | ${CMAKE_CURRENT_SOURCE_DIR}/src/cpu_utils_win.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/src/os_utils_win.cpp 16 | ) 17 | elseif(UNIX) 18 | if(APPLE) 19 | message("Configuring OS utilities for Mac") 20 | target_sources(lcxx_ident_utils PUBLIC 21 | ${CMAKE_CURRENT_SOURCE_DIR}/src/cpu_utils_mac.cpp 22 | ${CMAKE_CURRENT_SOURCE_DIR}/src/os_utils_mac.cpp 23 | ) 24 | else() 25 | message("Configuring OS utilities for Linux") 26 | target_sources(lcxx_ident_utils PUBLIC 27 | ${CMAKE_CURRENT_SOURCE_DIR}/src/cpu_utils_linux.cpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/src/os_utils_linux.cpp 29 | ) 30 | 31 | endif() 32 | endif() 33 | 34 | add_library(lcxx::ident_utils ALIAS lcxx_ident_utils) -------------------------------------------------------------------------------- /modules/ident_utils/include/ident_utils/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENT_UTILS_COMMON_HPP__ 2 | #define LCXX__IDENT_UTILS_COMMON_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lcxx::experimental::ident_utils { 10 | 11 | inline auto cat_file( std::string const & file_path ) -> std::string 12 | { 13 | std::ifstream ifs{ file_path }; 14 | std::stringstream ss( "" ); 15 | ss << ifs.rdbuf(); 16 | return ss.str(); 17 | }; 18 | 19 | template < std::size_t BufferSize = 1024 > 20 | inline auto c2s_wrapper( std::invocable< char *, std::size_t > auto f ) -> std::string 21 | { 22 | std::string tmp( BufferSize, 0 ); 23 | f( tmp.data(), BufferSize ); 24 | tmp.resize( std::strlen( tmp.c_str() ) ); 25 | return tmp; 26 | } 27 | 28 | } // namespace lcxx::experimental::ident_utils 29 | 30 | #endif // LCXX__IDENT_UTILS_COMMON_HPP__ 31 | -------------------------------------------------------------------------------- /modules/ident_utils/include/ident_utils/cpu_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENT_UTILS_CPU_UTILS_HPP__ 2 | #define LCXX__IDENT_UTILS_CPU_UTILS_HPP__ 3 | 4 | #include 5 | 6 | namespace lcxx::experimental::ident_utils::cpu { 7 | 8 | struct cpu_info { 9 | std::size_t n_cores; 10 | std::size_t n_threads; 11 | std::size_t max_frequency; 12 | std::string vendor; 13 | std::string model_name; 14 | }; 15 | 16 | /** 17 | * @brief retrieves information about the CPU built into this machine 18 | * 19 | * @return cpu_info an information object about the CPU 20 | */ 21 | auto get_info() -> cpu_info; 22 | 23 | } // namespace lcxx::experimental::ident_utils::cpu 24 | 25 | #endif // LCXX__IDENT_UTILS_CPU_UTILS_HPP__ 26 | -------------------------------------------------------------------------------- /modules/ident_utils/include/ident_utils/os_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENT_UTILS_OS_UTILS_HPP__ 2 | #define LCXX__IDENT_UTILS_OS_UTILS_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace lcxx::experimental::ident_utils::os { 8 | 9 | struct os_info { 10 | std::string os_name; 11 | std::string os_architecture; 12 | std::string os_pc_name; 13 | std::string user_name; 14 | std::vector< std::string > user_groups; 15 | std::size_t user_uid; 16 | std::vector< std::size_t > user_gids; 17 | }; 18 | 19 | /** 20 | * @brief retrieves information about the CPU built into this machine 21 | * 22 | * @return os_info an information object about the OS 23 | */ 24 | auto get_info() -> os_info; 25 | 26 | } // namespace lcxx::experimental::ident_utils::os 27 | 28 | #endif // LCXX__IDENT_UTILS_OS_UTILS_HPP__ 29 | -------------------------------------------------------------------------------- /modules/ident_utils/src/cpu_utils_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace lcxx::experimental::ident_utils::cpu { 9 | 10 | auto get_info() -> cpu_info 11 | { 12 | cpu_info ci; 13 | 14 | auto try_stoull = []( std::string const & str ) -> unsigned long long { 15 | try { 16 | return std::stoull( str ); 17 | } 18 | catch ( std::invalid_argument & e ) { 19 | // Should maybe throw again? Or just leave these entries empty 20 | // Having them empty does not break systems that do not have permissions to access these files 21 | return {}; 22 | } 23 | }; 24 | 25 | ci.max_frequency = try_stoull( cat_file( "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" ) ); 26 | ci.n_threads = std::thread::hardware_concurrency(); 27 | auto cpu_info = cat_file( "/proc/cpuinfo" ); 28 | 29 | auto parse_cpu_info_attr = []( std::string const & info, std::string_view const key ) -> std::string { 30 | std::string_view info_v = info; 31 | if ( auto pos = info_v.find( key ); pos != std::string::npos ) { 32 | info_v.remove_prefix( pos ); 33 | info_v.remove_prefix( info_v.find( ":" ) + 1 ); 34 | info_v.remove_suffix( info_v.size() - info_v.find( '\n' ) ); 35 | return std::string{ info_v }; 36 | } 37 | 38 | return {}; 39 | }; 40 | 41 | ci.vendor = parse_cpu_info_attr( cpu_info, "vendor_id" ); 42 | ci.model_name = parse_cpu_info_attr( cpu_info, "model name" ); 43 | ci.n_cores = try_stoull( parse_cpu_info_attr( cpu_info, "cpu cores" ) ); 44 | 45 | return ci; 46 | } 47 | 48 | } // namespace lcxx::experimental::ident_utils::cpu 49 | -------------------------------------------------------------------------------- /modules/ident_utils/src/cpu_utils_mac.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace lcxx::experimental::ident_utils::cpu { 6 | 7 | auto get_info() -> cpu_info 8 | { 9 | throw std::runtime_error( "This feature is not yet implemented." ); 10 | return {}; 11 | } 12 | 13 | } // namespace lcxx::experimental::ident_utils::cpu 14 | -------------------------------------------------------------------------------- /modules/ident_utils/src/cpu_utils_win.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace lcxx::experimental::ident_utils::cpu { 6 | 7 | auto get_info() -> cpu_info 8 | { 9 | throw std::runtime_error( "This feature is not yet implemented." ); 10 | return {}; 11 | } 12 | 13 | } // namespace lcxx::experimental::ident_utils::cpu 14 | -------------------------------------------------------------------------------- /modules/ident_utils/src/os_utils_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace lcxx::experimental::ident_utils::os { 12 | 13 | auto get_info() -> os_info 14 | { 15 | os_info oi; 16 | 17 | oi.user_name = c2s_wrapper( []( auto buffer, auto len ) { getlogin_r( buffer, len ); } ); 18 | oi.user_uid = getuid(); 19 | 20 | int buffer_len = 64; 21 | std::vector< gid_t > group_ids( buffer_len, 0 ); 22 | getgrouplist( oi.user_name.c_str(), getgid(), group_ids.data(), &buffer_len ); 23 | std::ranges::transform( group_ids | std::views::take( buffer_len ), std::back_inserter( oi.user_gids ), 24 | []( auto const id ) -> std::size_t { return id; } ); 25 | std::ranges::sort( oi.user_gids ); 26 | 27 | for ( auto const gid : oi.user_gids ) { 28 | c2s_wrapper( [&]( auto buffer, auto len ) { 29 | struct group g; 30 | struct group * g_ptr; 31 | getgrgid_r( gid, &g, buffer, len, &g_ptr ); 32 | if ( g_ptr ) { 33 | oi.user_groups.push_back( g.gr_name ); 34 | } 35 | else { 36 | oi.user_groups.push_back( "" ); 37 | } 38 | } ); 39 | } 40 | 41 | // Do I need to manually free the memory? 42 | struct utsname uts_name; 43 | uname( &uts_name ); 44 | oi.os_architecture = uts_name.machine; 45 | oi.os_name = uts_name.sysname; 46 | 47 | oi.os_pc_name = c2s_wrapper( []( auto buf, auto len ) { gethostname( buf, len ); } ); 48 | 49 | return oi; 50 | } 51 | 52 | } // namespace lcxx::experimental::ident_utils::os 53 | -------------------------------------------------------------------------------- /modules/ident_utils/src/os_utils_mac.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace lcxx::experimental::ident_utils::os { 6 | 7 | auto get_info() -> os_info 8 | { 9 | throw std::runtime_error( "This feature is not yet implemented." ); 10 | return {}; 11 | } 12 | 13 | } // namespace lcxx::experimental::ident_utils::os 14 | -------------------------------------------------------------------------------- /modules/ident_utils/src/os_utils_win.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace lcxx::experimental::ident_utils::os { 6 | 7 | auto get_info() -> os_info 8 | { 9 | throw std::runtime_error( "This feature is not yet implemented." ); 10 | return {}; 11 | } 12 | 13 | } // namespace lcxx::experimental::ident_utils::os 14 | -------------------------------------------------------------------------------- /modules/identifiers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lcxx_identifiers 2 | ${CMAKE_CURRENT_SOURCE_DIR}/src/hardware.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/src/os.cpp 4 | ) 5 | 6 | target_link_libraries( lcxx_identifiers PUBLIC 7 | lcxx::ident_utils 8 | lcxx::license 9 | lcxx::crypto 10 | nlohmann_json::nlohmann_json 11 | ) 12 | 13 | target_include_directories( lcxx_identifiers PUBLIC 14 | ${CMAKE_CURRENT_SOURCE_DIR}/include 15 | ${VERSION_DIR} 16 | ) 17 | 18 | target_compile_features(lcxx_identifiers PUBLIC cxx_std_20) 19 | set_target_properties(lcxx_identifiers PROPERTIES CMAKE_CXX_STANDARD_REQUIRED ON) 20 | 21 | 22 | add_library(lcxx::identifiers ALIAS lcxx_identifiers) -------------------------------------------------------------------------------- /modules/identifiers/include/lcxx/identifiers/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENTIFIERS_COMMON_HPP__ 2 | #define LCXX__IDENTIFIERS_COMMON_HPP__ 3 | 4 | #include 5 | 6 | namespace lcxx::experimental::identifiers { 7 | 8 | template < typename Strat_Enum > 9 | requires( std::is_enum< Strat_Enum >::value ) auto strat_active( Strat_Enum strat, Strat_Enum query ) -> bool 10 | { 11 | using base_t = std::underlying_type_t< Strat_Enum >; 12 | return static_cast< base_t >( strat ) & static_cast< base_t >( query ); 13 | }; 14 | 15 | } // namespace lcxx::experimental::identifiers 16 | 17 | #endif // LCXX__IDENTIFIERS_COMMON_HPP__ -------------------------------------------------------------------------------- /modules/identifiers/include/lcxx/identifiers/hardware.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENTIFIERS_HARDWARE_HPP__ 2 | #define LCXX__IDENTIFIERS_HARDWARE_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace lcxx::experimental::identifiers { 11 | 12 | enum class hw_ident_strat : ident_strat_t { 13 | all = 1, 14 | cpu = 1 << 1, 15 | // gpu = 1 << 2, 16 | // ethernet = 1 << 3, 17 | cpu_n_cores = 1 << 10, 18 | cpu_n_threads = 1 << 11, 19 | cpu_max_frequency = 1 << 12, 20 | cpu_vendor = 1 << 13, 21 | cpu_model_name = 1 << 14, 22 | }; 23 | 24 | auto operator|( hw_ident_strat const lhs, hw_ident_strat const rhs ) -> hw_ident_strat; 25 | 26 | /** 27 | * @brief analyzes the host machine and creates an identification string from the gathered information. The analyzed 28 | * aspects depend on the strategy chosen 29 | * 30 | * @param strategy the analyzed hardware aspects. Different strategies can be combined through bitwise-or| 31 | * @return identifier the identifier containing a hash and the clear text result of the hardware analysis 32 | */ 33 | auto hardware( hw_ident_strat const strategy = hw_ident_strat::all ) -> identifier; 34 | 35 | /** 36 | * @brief verifies a hardware identification hash against the hardware this software is run on with respect to the 37 | * identification strategy chosen 38 | * 39 | * @param strategy the strategy chosen to identify the hardware. Must match the strategy used to generate the hash 40 | * @param hash the hash produced by a call to `hardware()` 41 | * @return true if the current hardware matches the one used to generate the hash 42 | * @return false otherwise 43 | */ 44 | auto verify( hw_ident_strat const strategy, std::string_view const hash ) -> bool; 45 | 46 | } // namespace lcxx::experimental::identifiers 47 | 48 | #endif // LCXX__IDENTIFIERS_HARDWARE_HPP__ 49 | -------------------------------------------------------------------------------- /modules/identifiers/include/lcxx/identifiers/identifier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENTIFIERS_IDENTIFIER_HPP__ 2 | #define LCXX__IDENTIFIERS_IDENTIFIER_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace lcxx::experimental::identifiers { 8 | 9 | using ident_strat_t = std::int16_t; 10 | 11 | enum class error { 12 | ok, 13 | hash_error, 14 | }; 15 | 16 | struct identifier { 17 | error err; 18 | std::string hash; 19 | std::string source_text; 20 | ident_strat_t ident_strat; 21 | }; 22 | 23 | } // namespace lcxx::experimental::identifiers 24 | 25 | #endif // LCXX__IDENTIFIERS_IDENTIFIER_HPP__ 26 | -------------------------------------------------------------------------------- /modules/identifiers/include/lcxx/identifiers/os.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__IDENTIFIERS_USER_HPP__ 2 | #define LCXX__IDENTIFIERS_USER_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace lcxx::experimental::identifiers { 11 | 12 | enum class os_ident_strat : ident_strat_t { 13 | all = 1, 14 | os_name = 1 << 1, 15 | os_architecture = 1 << 2, 16 | os_pc_name = 1 << 3, 17 | user_name = 1 << 11, 18 | user_uid = 1 << 12, 19 | user_groups = 1 << 13, 20 | user_gids = 1 << 14, 21 | os = os_name | os_architecture | os_pc_name, 22 | user = user_name | user_uid | user_groups | user_gids, 23 | 24 | }; 25 | 26 | auto operator|( os_ident_strat const lhs, os_ident_strat const rhs ) -> os_ident_strat; 27 | 28 | /** 29 | * @brief polls current os info from the operating system and generates an identifier based on the chosen os 30 | * identification strategy 31 | * 32 | * @param strategy the analyzed os features. Different strategies can be combined through bitwise-or| 33 | * @return identifier the identifier containing a hash and the clear text result of the os analysis 34 | */ 35 | auto os( os_ident_strat const strategy = os_ident_strat::all ) -> identifier; 36 | 37 | /** 38 | * @brief verifies a os identification hash against the os this software is run on with respect to the 39 | * identification strategy chosen 40 | * 41 | * @param strategy the strategy chosen to identify os info. Must match the strategy used to generate the hash 42 | * @param hash the hash produced by a call to `os()` 43 | * @return true if the current os environment matches the one used to generate the hash 44 | * @return false otherwise 45 | */ 46 | auto verify( os_ident_strat const strategy, std::string_view const hash ) -> bool; 47 | 48 | } // namespace lcxx::experimental::identifiers 49 | 50 | #endif // LCXX__IDENTIFIERS_USER_HPP__ -------------------------------------------------------------------------------- /modules/identifiers/src/hardware.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lcxx::experimental::ident_utils::cpu { 11 | NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( cpu_info, vendor, model_name, n_cores, n_threads, max_frequency ); 12 | } 13 | namespace lcxx::experimental::identifiers { 14 | 15 | auto operator|( hw_ident_strat const lhs, hw_ident_strat const rhs ) -> hw_ident_strat 16 | { 17 | using base_t = std::underlying_type_t< hw_ident_strat >; 18 | return static_cast< hw_ident_strat >( static_cast< base_t >( lhs ) | static_cast< base_t >( rhs ) ); 19 | } 20 | 21 | auto hardware( hw_ident_strat const strategy ) -> identifier 22 | { 23 | auto cpu_info = ident_utils::cpu::get_info(); 24 | 25 | nlohmann::json info_json; 26 | 27 | if ( strat_active( strategy, hw_ident_strat::all ) ) { 28 | auto msg = nlohmann::json{ cpu_info }.dump(); 29 | auto [hash, err] = hash::sha512( msg ); 30 | if ( err != hash::error::ok ) 31 | return { error::hash_error, {}, msg }; 32 | return { error::ok, encode::base64( hash ), msg }; 33 | } 34 | 35 | if ( strat_active( strategy, hw_ident_strat::cpu ) ) { 36 | info_json["cpu"] = { cpu_info }; 37 | } 38 | else { 39 | if ( strat_active( strategy, hw_ident_strat::cpu_n_cores ) ) 40 | info_json["n_cores"] = cpu_info.n_cores; 41 | if ( strat_active( strategy, hw_ident_strat::cpu_n_threads ) ) 42 | info_json["n_threads"] = cpu_info.n_threads; 43 | if ( strat_active( strategy, hw_ident_strat::cpu_max_frequency ) ) 44 | info_json["max_frequency"] = cpu_info.max_frequency; 45 | if ( strat_active( strategy, hw_ident_strat::cpu_vendor ) ) 46 | info_json["vendor"] = cpu_info.vendor; 47 | if ( strat_active( strategy, hw_ident_strat::cpu_model_name ) ) 48 | info_json["model_name"] = cpu_info.model_name; 49 | } 50 | 51 | auto msg = info_json.dump(); 52 | auto [hash, err] = hash::sha512( msg ); 53 | if ( err != hash::error::ok ) 54 | return { error::hash_error, {}, msg }; 55 | return { error::ok, encode::base64( hash ), msg }; 56 | // if(strat_active(strategy, hw_ident_strat::gpu) {} 57 | // if(strat_active(strategy, hw_ident_strat::network) {} 58 | } 59 | 60 | auto verify( hw_ident_strat const strategy, std::string_view const hash ) -> bool 61 | { 62 | return hash == hardware( strategy ).hash; 63 | } 64 | 65 | } // namespace lcxx::experimental::identifiers 66 | -------------------------------------------------------------------------------- /modules/identifiers/src/os.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lcxx::experimental::ident_utils::os { 10 | NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( os_info, os_name, os_architecture, os_pc_name, user_name, user_groups, user_uid, 11 | user_gids ); 12 | } 13 | 14 | namespace lcxx::experimental::identifiers { 15 | 16 | os_ident_strat operator|( os_ident_strat const lhs, os_ident_strat const rhs ) 17 | { 18 | using base_t = std::underlying_type_t< os_ident_strat >; 19 | return static_cast< os_ident_strat >( static_cast< base_t >( lhs ) | static_cast< base_t >( rhs ) ); 20 | } 21 | 22 | auto os( os_ident_strat const strategy ) -> identifier 23 | { 24 | auto os_info = ident_utils::os::get_info(); 25 | 26 | nlohmann::json id_json; 27 | 28 | if ( strat_active( strategy, os_ident_strat::all ) ) { 29 | auto msg = nlohmann::json{ os_info }.dump(); 30 | 31 | auto [hash, err] = hash::sha512( msg ); 32 | if ( err != hash::error::ok ) 33 | return { error::hash_error, {}, msg }; 34 | return { 35 | error::hash_error, 36 | encode::base64( hash ), 37 | msg, 38 | }; 39 | } 40 | 41 | if ( strat_active( strategy, os_ident_strat::os ) ) { 42 | id_json["os_name"] = os_info.os_name; 43 | id_json["os_architecture"] = os_info.os_architecture; 44 | id_json["os_pc_name"] = os_info.os_pc_name; 45 | } 46 | else { 47 | if ( strat_active( strategy, os_ident_strat::os_name ) ) 48 | id_json["os_name"] = os_info.os_name; 49 | if ( strat_active( strategy, os_ident_strat::os_architecture ) ) 50 | id_json["os_architecture"] = os_info.os_architecture; 51 | if ( strat_active( strategy, os_ident_strat::os_pc_name ) ) 52 | id_json["os_pc_name"] = os_info.os_pc_name; 53 | } 54 | if ( strat_active( strategy, os_ident_strat::user ) ) { 55 | id_json["user_name"] = os_info.user_name; 56 | id_json["user_groups"] = os_info.user_groups; 57 | id_json["user_gids"] = os_info.user_gids; 58 | } 59 | else { 60 | if ( strat_active( strategy, os_ident_strat::user_name ) ) 61 | id_json["user_name"] = os_info.user_name; 62 | if ( strat_active( strategy, os_ident_strat::user_groups ) ) 63 | id_json["user_groups"] = os_info.user_groups; 64 | if ( strat_active( strategy, os_ident_strat::user_gids ) ) 65 | id_json["user_gids"] = os_info.user_gids; 66 | } 67 | 68 | auto msg = id_json.dump(); 69 | auto [hash, err] = hash::sha512( msg ); 70 | if ( err != hash::error::ok ) 71 | return { error::hash_error, {}, msg }; 72 | return { error::ok, encode::base64( hash ), msg, static_cast< ident_strat_t >( strategy ) }; 73 | } 74 | 75 | auto verify( os_ident_strat const strategy, std::string_view const hash ) -> bool 76 | { 77 | return hash == os( strategy ).hash; 78 | } 79 | } // namespace lcxx::experimental::identifiers 80 | -------------------------------------------------------------------------------- /modules/lcxx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( licensecxx 2 | ${CMAKE_CURRENT_SOURCE_DIR}/src/writer.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/src/reader.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/src/verifier.cpp 5 | ) 6 | 7 | target_include_directories(licensecxx PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR}/include 9 | ) 10 | 11 | target_compile_features(licensecxx PUBLIC cxx_std_20) 12 | set_target_properties(licensecxx PROPERTIES CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | add_library( lcxx::lcxx ALIAS licensecxx ) 15 | 16 | target_link_libraries( licensecxx PUBLIC 17 | nlohmann_json::nlohmann_json 18 | lcxx::crypto 19 | lcxx::license 20 | ) 21 | -------------------------------------------------------------------------------- /modules/lcxx/include/lcxx/lcxx.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__LCXX_LCXX_HPP__ 2 | #define LCXX__LCXX_LCXX_HPP__ 3 | 4 | /// Convencience header 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #endif // LCXX__LCXX_LCXX_HPP__ 12 | -------------------------------------------------------------------------------- /modules/lcxx/include/lcxx/reader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__LCXX_READER_HPP__ 2 | #define LCXX__LCXX_READER_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace lcxx { 12 | 13 | /** 14 | * @brief parse license and license signature from a json object 15 | * 16 | * @param license_json the json object containing license and signature 17 | * @return std::pair< license, signature > license content and signature 18 | */ 19 | auto from_json( nlohmann::json const & license_json ) -> std::pair< license, signature >; 20 | 21 | /** 22 | * @brief parse license and license signature from a string serialized json object 23 | * 24 | * @param license_str the serialized json string object containing license and signature 25 | * @return std::pair< license, signature > license content and signature 26 | */ 27 | auto from_json( std::string const & license_str ) -> std::pair< license, signature >; 28 | 29 | /** 30 | * @brief parse license and license signature from a string serialized json object 31 | * 32 | * @param license_str the json object containing license and signature 33 | * @return std::pair< license, signature > license content and signature 34 | */ 35 | auto from_json( std::span< char > const license_str ) -> std::pair< license, signature >; 36 | 37 | /** 38 | * @brief parse license and license signature from a json file 39 | * 40 | * @param license_path the path to the json file 41 | * @return std::pair< license, signature > license content and signature 42 | */ 43 | auto from_json( std::filesystem::path const & license_path ) -> std::pair< license, signature >; 44 | 45 | } // namespace lcxx 46 | 47 | #endif // LCXX__LCXX_READER_HPP__ 48 | -------------------------------------------------------------------------------- /modules/lcxx/include/lcxx/verifier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__LCXX_VERIFIER_HPP__ 2 | #define LCXX__LCXX_VERIFIER_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace lcxx { 8 | 9 | /** 10 | * @brief sign the content of a license object with a private RSA key and return the signature 11 | * 12 | * @param lic the license object to sign 13 | * @param private_key the private key used to generate the signature 14 | * @return std::vector< std::byte > the binary signature in form of a byte vector 15 | */ 16 | auto sign( license const & lic, crypto::rsa_key_t const private_key ) -> std::vector< std::byte >; 17 | 18 | /** 19 | * @brief check the contents of a license object against a signature by using a public RSA key for decryption 20 | * 21 | * @param lic the license object to check against the signature 22 | * @param signature the signature of the original license object 23 | * @param public_key the public key which must correspond to the private key used for encryption 24 | * @return true if the license and public key match the original license and private key used for the signature 25 | * @return false otherwise 26 | */ 27 | auto verify_license( license const & lic, std::vector< std::byte > const & signature, 28 | crypto::rsa_key_t const public_key ) -> bool; 29 | 30 | } // namespace lcxx 31 | 32 | #endif // LCXX__LCXX_VERIFIER_HPP__ 33 | -------------------------------------------------------------------------------- /modules/lcxx/include/lcxx/writer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__LCXX_WRITER_HPP__ 2 | #define LCXX__LCXX_WRITER_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace lcxx { 13 | 14 | /** 15 | * @brief Calculates the signature and generates the output json 16 | * 17 | * @param license the license object, which will be serialized 18 | * @param private_key the private key used to create the signature 19 | * @return nlohmann::json a json object containing the serialized license and the signature 20 | */ 21 | auto to_json( license const & license, crypto::rsa_key_t const private_key ) -> nlohmann::json; 22 | 23 | /** 24 | * @brief Calculates the signature and generates the output json, writing it to the file pointed to by `output_file` 25 | * 26 | * @param license the license object, which will be serialized 27 | * @param output_file a system path pointing to the target file - will be overwritten, if it points to an existing 28 | * @param private_key the private key used to create the signature 29 | * file 30 | */ 31 | void to_json( license const & license, std::filesystem::path const & output_file, 32 | crypto::rsa_key_t const private_key ); 33 | 34 | } // namespace lcxx 35 | 36 | #endif // LCXX__LCXX_WRITER_HPP__ -------------------------------------------------------------------------------- /modules/lcxx/src/reader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace lcxx { 8 | 9 | auto from_json( nlohmann::json const & license_json ) -> std::pair< license, signature > 10 | { 11 | license l; 12 | 13 | if ( !license_json.contains( signature_key ) || !license_json.contains( content_key ) ) { 14 | throw std::invalid_argument( std::string{ "The input json does not contain they keys " } + signature_key + 15 | " and " + content_key ); 16 | } 17 | 18 | auto const & content = license_json[content_key]; 19 | for ( auto const & item : content.items() ) { 20 | l.push_content( item.key(), item.value().get< std::string >() ); 21 | } 22 | return { l, lcxx::decode::base64( license_json[signature_key].get< std::string >() ) }; 23 | } 24 | 25 | auto from_json( std::string const & license_str ) -> std::pair< license, signature > 26 | { 27 | return from_json( nlohmann::json::parse( license_str.begin(), license_str.end() ) ); 28 | } 29 | 30 | auto from_json( std::span< char > const license_str ) -> std::pair< license, signature > 31 | { 32 | return from_json( nlohmann::json::parse( license_str.begin(), license_str.end() ) ); 33 | } 34 | 35 | auto from_json( std::filesystem::path const & license_path ) -> std::pair< license, signature > 36 | { 37 | return from_json( 38 | nlohmann::json::parse( std::ifstream( std::filesystem::absolute( license_path ).string() ) ) ); 39 | } 40 | 41 | } // namespace lcxx -------------------------------------------------------------------------------- /modules/lcxx/src/verifier.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace lcxx { 6 | 7 | auto sign( license const & lic, crypto::rsa_key_t const private_key ) -> std::vector< std::byte > 8 | { 9 | return crypto::sign( lic.stringify(), private_key ); 10 | } 11 | 12 | auto verify_license( license const & lic, std::vector< std::byte > const & signature, 13 | crypto::rsa_key_t const public_key ) -> bool 14 | { 15 | return crypto::verify_signature( lic.stringify(), signature, public_key ); 16 | } 17 | 18 | } // namespace lcxx 19 | -------------------------------------------------------------------------------- /modules/lcxx/src/writer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace lcxx { 9 | 10 | nlohmann::json to_json( license const & license, crypto::rsa_key_t const private_key ) 11 | { 12 | auto const signature = lcxx::sign( license, private_key ); 13 | nlohmann::json lic_json = { 14 | { signature_key, encode::base64( signature ) }, 15 | { content_key, {} }, 16 | }; 17 | 18 | auto & content = lic_json[content_key]; 19 | for ( auto const & [k, v] : license.get_content() ) { 20 | content[k] = v; 21 | } 22 | 23 | return lic_json; 24 | } 25 | 26 | void to_json( license const & license, std::filesystem::path const & output_file, 27 | crypto::rsa_key_t const private_key ) 28 | { 29 | namespace fs = std::filesystem; 30 | if ( !output_file.has_filename() ) { 31 | throw std::invalid_argument( std::string{ "the given output file must point to a file location: " } + 32 | fs::absolute( output_file ).string() ); 33 | } 34 | 35 | auto lic_json = to_json( license, private_key ); 36 | 37 | std::ofstream ofs{ fs::absolute( output_file ).string() }; 38 | ofs << lic_json.dump() << std::endl; 39 | } 40 | } // namespace lcxx 41 | -------------------------------------------------------------------------------- /modules/license/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lcxx_license INTERFACE) 2 | 3 | target_include_directories(lcxx_license INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/include 5 | ${VERSION_DIR} 6 | ) 7 | 8 | target_compile_features(lcxx_license INTERFACE cxx_std_20) 9 | set_target_properties(lcxx_license PROPERTIES CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | add_library( lcxx::license ALIAS lcxx_license ) 12 | 13 | target_link_libraries( lcxx_license INTERFACE 14 | nlohmann_json::nlohmann_json 15 | ) 16 | -------------------------------------------------------------------------------- /modules/license/include/lcxx/license.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__LCXX_LICENSE_HPP__ 2 | #define LCXX__LCXX_LICENSE_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace lcxx { 15 | 16 | constexpr auto signature_key = "signature"; 17 | constexpr auto content_key = "content"; 18 | 19 | template < typename T > 20 | concept string_convertable = std::is_same_v< T, std::string > || std::is_integral_v< T >() || 21 | std::is_floating_point_v< T >() || std::is_enum_v< T >() || std::is_convertible_v< T, std::string >; 22 | 23 | using signature = std::vector< std::byte >; 24 | using content_map_t = std::unordered_map< std::string, std::string >; 25 | 26 | /** 27 | * @brief A general license data class 28 | * This class describes a data object that holds arbitrary key-value string parameters. 29 | * Fundamentally, it's a restricted std::unordered_map 30 | */ 31 | class license { 32 | 33 | public: 34 | license( std::unordered_map< std::string, std::string > const & content ) : content_( content ) {} 35 | license() : content_() {} 36 | 37 | /** 38 | * @brief Adds content to the license. This collection of key-value pairs will be used to generate a signature 39 | * 40 | * @tparam S any value that is either a number, string or convertible to a string 41 | * @param key a key tag 42 | * @param value the corresponding value 43 | * @return true if the key has not existed before and was inserted with a new value 44 | * @return false if the key has existed already and been overwritten with a new value 45 | */ 46 | template < string_convertable S > auto push_content( std::string const & key, S const & value ) -> bool 47 | { 48 | auto func = [this]( std::string const & k, std::string const & v ) { 49 | return content_.insert_or_assign( k, v ).second; 50 | }; 51 | if constexpr ( std::is_same< S, std::string >() ) 52 | return func( key, value ); 53 | if constexpr ( std::is_integral< S >() || std::is_floating_point< S >() ) 54 | return func( key, std::to_string( value ) ); 55 | if constexpr ( std::is_enum< S >() ) 56 | return func( key, std::to_string( static_cast< std::underlying_type< S > >( value ) ) ); 57 | if constexpr ( std::is_convertible< S, std::string >() ) 58 | return func( key, std::string{ value } ); 59 | 60 | // Should be unreachable 61 | throw std::runtime_error( "Unexpected type provided." ); 62 | } 63 | template < string_convertable S > auto push_content( std::pair< std::string, S > const & kv ) -> bool 64 | { 65 | return push_content( kv.first, kv.second ); 66 | } 67 | 68 | /** 69 | * @brief get a const access to the underlying map object 70 | * 71 | * @return content_map_t const& the underlying map object 72 | */ 73 | auto get_content() const -> content_map_t const & { return content_; } 74 | 75 | /** 76 | * @brief read a single parameter from the map 77 | * 78 | * @param key the key to search for 79 | * @return std::optional either the value corresponding to `key` or std::nullopt if 80 | * that key does not exist 81 | */ 82 | auto get( std::string const & key ) -> std::optional< content_map_t::mapped_type > 83 | { 84 | if ( content_.contains( key ) ) 85 | return content_[key]; 86 | else 87 | return {}; 88 | } 89 | 90 | /** 91 | * @brief serialize the license to a single string that is reproducable if the contents of the license are equal 92 | * 93 | * @return std::string a single string version of the full license content 94 | */ 95 | auto stringify() const -> std::string 96 | { 97 | std::vector< std::pair< std::string, std::string > > sorted_content{ content_.begin(), content_.end() }; 98 | std::sort( sorted_content.begin(), sorted_content.end() ); 99 | 100 | std::string tmp; 101 | for ( auto const & [k, v] : sorted_content ) { 102 | tmp += k + ":" + v; 103 | } 104 | return tmp; 105 | } 106 | 107 | private: 108 | content_map_t content_; 109 | }; 110 | 111 | } // namespace lcxx 112 | 113 | #endif // LCXX__LCXX_LICENSE_HPP__ -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ident_utils) 2 | add_subdirectory(license_generator) 3 | add_subdirectory(license_verifier) 4 | 5 | add_custom_target(SAMPLES DEPENDS license_generator license_verifier ident_utils_samples) 6 | -------------------------------------------------------------------------------- /samples/ident_utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(ident_utils_samples 2 | ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp 3 | ) 4 | target_link_libraries(ident_utils_samples PRIVATE 5 | lcxx::ident_utils 6 | nlohmann_json::nlohmann_json 7 | ) 8 | -------------------------------------------------------------------------------- /samples/ident_utils/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | using namespace lcxx::experimental::ident_utils; 9 | 10 | namespace lcxx::experimental::ident_utils::cpu { 11 | NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( cpu_info, vendor, model_name, n_cores, n_threads, max_frequency ); 12 | } 13 | namespace lcxx::experimental::ident_utils::os { 14 | NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( os_info, os_name, os_architecture, os_pc_name, user_name, user_groups, user_uid, 15 | user_gids ); 16 | } 17 | 18 | auto main() -> int 19 | { 20 | auto print = []( auto const & desc, auto const & info ) { 21 | std::cout << desc << ": " << nlohmann::json( info ).dump( 4 ) << std::endl; 22 | }; 23 | 24 | print( "cpu", cpu::get_info() ); 25 | print( "os", os::get_info() ); 26 | // print( "disk", disk::get_info() ); 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /samples/license_generator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(license_generator 2 | ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp 3 | ) 4 | target_link_libraries(license_generator 5 | lcxx::lcxx 6 | lcxx::identifiers 7 | ) 8 | -------------------------------------------------------------------------------- /samples/license_generator/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Can also be loaded from file 8 | constexpr auto private_key = R"( 9 | -----BEGIN RSA PRIVATE KEY----- 10 | MIIEowIBAAKCAQEArT6TBfYmVOrE38Y1FIwzW78Xkp8PQWCfJyjAjPWQafXznTuG 11 | 1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4ezkoS3i+s9fMmbTzLgy3MceZa+aZk 12 | CZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piSYDjdIIr+LvbGFlB+95BhsEwq35sD 13 | ap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCrlxGSGOV4cz7T59tHCXOS2SXSechm 14 | yONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa9D45xGNCUXttwhT2HaZmEpCfjEuu 15 | 1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4MajQIDAQABAoIBADaf3Eus6TktLRZB 16 | sMrhye/LuOsKLA05IfV+6/eGFhrS6hVRh5nOix80OCchlHpLo+BTWXOfSfn6Os9Z 17 | Dqu85Am3YWudLLQADsWyvZiHAEeJknXU+QXtmrrjpQp9lNGR4YuGVOo8v+Ra5qfv 18 | 5MQ5Ry3/v/la2fqE5MB0cRQt+h9T3/8P0dJm9COJGF3msX4ALzj4l2+GOxpfqTBF 19 | BlrlOc/ndEb2uUT7t4xesFeJi140E0Gl0IiFXNGd5jmk1jTGpvypkYsfqLqnxuEE 20 | 2i5CGSof2rvKS9TQvHu2ayw/HquBSzqyxYJUQl+/7BZneVl+WFwSeqfDfbzSZRX8 21 | 8B5u6QECgYEA1boG7DFCb2sVOmh0Xu7fPYwAOBaYsJpv56i/evVuUSevilB+RK/+ 22 | Gy+lvoyCsx8jG/tMOG0eWtNADV/O2FE6/jZTNgZj0Th08xMk/+2cUryxdgHsLflj 23 | bCX6CIv/mbo5H2TFPzeYnfB9wEnKUIo/W1CLi/GzuloF8wLjJ3mASxUCgYEAz4K+ 24 | Zkk1S2F7BXR9KECDu/dThKDKFe4hCJMIFNplVhwdfm/DEulZJT5TgUZoQ9SUMAfW 25 | cWCNbzXCBAHyYz3TLNBjpG1Ql4O1NwbzQPBSsGc1fPNDjBRYaVKHqj2UB8c+7+aI 26 | toPxHDTKuCvJGAW1sJfKgL5KSZ0CyfT0nRewD5kCgYAIDG1eT6yUzY+LF4vqV0yI 27 | 4NDRS+iMHgTA0JYFZ2C0Ja5yov1eUNJc67puJpR1cmK8FwaTyWgvO21aE5WSh9yU 28 | 3i7cBfmUU2/0B3CJQsV0SC7WptPiF1YrKHL2B2+ktmKYUA8thkZ1DC1wJFc+GTax 29 | laLrrjp6dhFrSVyMjALM0QKBgQDAr6ALLNl/CKKKWzPIh7eLd8qmsgNUv80OwDDV 30 | 5EIK5oqAmFjkm6e6jJhPx0gUDDYaL3zxxH0wkhN5UzF287a+uzZ7PUKDnrpLwXlp 31 | iH7P7NZfEyhaz52VFxyAeTOW0W3gqAm0qOnfjinbQFU3qD2hICHa0Ff86P3o+DuY 32 | D6HdqQKBgC+/j9dvFMoB8oE3zpA7D20Wh3qSFUf7cBD3FmBg89+qIiYvitGnTY0k 33 | CuOsl7rQhL0KAr4yfYigxgeBQXEowRVTLdhXNQBb5ux5qX0T9PpJHkoIVpwGspf+ 34 | 7BNtOAIpetC5vz4UTaNWiDxFiqg0zlT7+YCVyDMNPHyuAas687gP 35 | -----END RSA PRIVATE KEY----- 36 | )"; 37 | 38 | auto main() -> int 39 | { 40 | lcxx::license license; 41 | 42 | // Push optional data into the license file 43 | license.push_content( "some key", "some value" ); 44 | 45 | license.push_content( "user", "SomeUsername" ); 46 | license.push_content( "email", "SomeMail@SomeProvider.org" ); 47 | 48 | auto key = lcxx::crypto::load_key( std::string{ private_key }, lcxx::crypto::key_type::private_key ); 49 | std::cout << lcxx::to_json( license, key ).dump( 4 ) << std::endl; 50 | } -------------------------------------------------------------------------------- /samples/license_verifier/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(license_verifier 2 | ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp 3 | ) 4 | target_link_libraries(license_verifier 5 | lcxx::lcxx 6 | ) 7 | -------------------------------------------------------------------------------- /samples/license_verifier/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | // Can both be loaded from file or elsewhere 6 | constexpr auto lic_str = R"( 7 | { 8 | "content": { 9 | "another key": "another value", 10 | "email": "john.doe@happymail.com", 11 | "some key": "some value" 12 | }, 13 | "signature": "f4puZx7UnIHPM+LskRDBLzLHbgKaTuMHYRGl/F5xCInrh1hs36bPh8ONtvBvZS1HmCww8jX+NPGdzfVInmJn/C+1ptdirJ/FBPEx0HGbpNSmEMzVguL0DO8Kvb4zhO1SiiZVKBfE1rTGlcf06hfwDcw0krx+dXaWWYeXqhDeGD+m/2ihNs+r0YgLkRwFRAbDUxz2xRsrobdfB3D178a8i0W8ElwY0tfy38OF4woSsZUNpyiFreIHzGbqLs/P5L262/N6zs98H65ZWOF7KDLiMc7tMOIXptlMxk/SNTncrlCZ71Kzgq2/4hVRzNbWeu+thJA21YTSdOTYk8d5QoarRQ==" 14 | } 15 | )"; 16 | 17 | constexpr auto public_key = R"( 18 | -----BEGIN PUBLIC KEY----- 19 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArT6TBfYmVOrE38Y1FIwz 20 | W78Xkp8PQWCfJyjAjPWQafXznTuG1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4e 21 | zkoS3i+s9fMmbTzLgy3MceZa+aZkCZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piS 22 | YDjdIIr+LvbGFlB+95BhsEwq35sDap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCr 23 | lxGSGOV4cz7T59tHCXOS2SXSechmyONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa 24 | 9D45xGNCUXttwhT2HaZmEpCfjEuu1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4Ma 25 | jQIDAQAB 26 | -----END PUBLIC KEY----- 27 | )"; 28 | 29 | auto main() -> int 30 | { 31 | 32 | auto key = lcxx::crypto::load_key( std::string{ public_key }, lcxx::crypto::key_type::public_key ); 33 | auto [license, signature] = lcxx::from_json( std::string{ lic_str } ); 34 | 35 | if ( !lcxx::verify_license( license, signature, key ) ) { 36 | std::cout << "This program is not licensed!" << std::endl; 37 | return -1; 38 | } 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /scripts/generate_headers.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import pathlib 4 | import sys 5 | 6 | 7 | def generate_headers(priv_key_path: Path, pub_key_path: Path, output_dir: Path): 8 | print("Generating source header from Key files") 9 | print(f"Private key: {priv_key_path.absolute()}") 10 | print(f"Public key: {pub_key_path.absolute()}") 11 | print(f"Output dir: {output_dir.absolute()}") 12 | 13 | output_dir = output_dir / Path("keys") 14 | 15 | if not output_dir.exists(): 16 | os.makedirs(output_dir.absolute()) 17 | 18 | with open(priv_key_path.absolute(), 'r') as private_key_file: 19 | with open(output_dir.absolute() / Path("private_key.hpp"), 'w') as priv_output: 20 | private_key = "".join(private_key_file.read().split()) 21 | # TODO: Process the file to something useful 22 | priv_output.write( 23 | '#ifndef LCXX_PRIVATE_KEY_H__\n' 24 | '#define LCXX_PRIVATE_KEY_H__\n' 25 | '#endif // LCXX_PRIVATE_KEY_H__\n\n' 26 | '// This file is automatically generated during build. Do not edit ...\n\n' 27 | 'namespace lcxx::keys {\n\n' 28 | f'\tconstexpr auto private_key = "{private_key}";\n\n' 29 | '} // namespace lcxx::keys\n' 30 | ) 31 | 32 | with open(pub_key_path.absolute(), 'r') as public_key_file: 33 | with open(output_dir.absolute() / Path("public_key.hpp"), 'w') as pub_output: 34 | pub_key = "".join(public_key_file.read().split()) 35 | # TODO: Process the file to something useful 36 | pub_output.write( 37 | '#ifndef LCXX_PUBLIC_KEY_H__\n' 38 | '#define LCXX_PUBLIC_KEY_H__\n' 39 | '#endif // LCXX_PUBLIC_KEY_H__\n\n' 40 | '// This file is automatically generated during build. Do not edit ...\n\n' 41 | 'namespace lcxx::keys {\n\n' 42 | f'\tconstexpr auto public_key = "{pub_key}";\n\n' 43 | '} // namespace lcxx::keys\n' 44 | ) 45 | 46 | 47 | if __name__ == "__main__": 48 | if len(sys.argv) < 4: 49 | print(f"Not enough cli arguments provided. Arguements: [{sys.argv}]") 50 | exit(-1) 51 | 52 | generate_headers(Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3])) 53 | -------------------------------------------------------------------------------- /scripts/version.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lcxx::version { 6 | 7 | constexpr std::string_view semantic = "${GIT_VER_SEM}"; 8 | constexpr std::string_view str = "${GIT_VER_STR}"; 9 | constexpr std::string_view base = "${GIT_VER_BASE}"; 10 | constexpr int major = ${GIT_VER_MAJOR}; 11 | constexpr int minor = ${GIT_VER_MINOR}; 12 | constexpr int build = ${GIT_VER_BUILD}; 13 | constexpr std::string_view tail = "${GIT_VER_TAIL}"; 14 | 15 | } // namespace lcxx -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(lcxx_tests) 4 | 5 | include(FetchContent) 6 | FetchContent_Declare( 7 | googletest 8 | GIT_REPOSITORY https://github.com/google/googletest.git 9 | GIT_TAG release-1.12.1 10 | ) 11 | 12 | # For Windows: Prevent overriding the parent project's compiler/linker settings 13 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 14 | FetchContent_MakeAvailable(googletest) 15 | 16 | add_subdirectory(crypto) 17 | add_subdirectory(identifiers) 18 | add_subdirectory(lcxx) 19 | 20 | add_custom_target(TESTS DEPENDS 21 | crypto_test 22 | identifiers_test 23 | lcxx_test 24 | ) -------------------------------------------------------------------------------- /tests/common/fixtures.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__TESTS_COMMON_FIXTURES_HPP__ 2 | #define LCXX__TESTS_COMMON_FIXTURES_HPP__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class key_fixture : public ::testing::Test { 10 | protected: 11 | lcxx::crypto::rsa_key_t private_key; 12 | lcxx::crypto::rsa_key_t public_key; 13 | 14 | key_fixture() : 15 | private_key( lcxx::crypto::load_key( std::string{ lcxx::tests::keys::private_key_str }, 16 | lcxx::crypto::key_type::private_key ) ), 17 | public_key( lcxx::crypto::load_key( std::string{ lcxx::tests::keys::public_key_str }, 18 | lcxx::crypto::key_type::public_key ) ) 19 | { 20 | } 21 | }; 22 | 23 | #endif // LCXX__TESTS_COMMON_FIXTURES_HPP__ 24 | -------------------------------------------------------------------------------- /tests/common/keys.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCXX__TESTS_COMMON_KEYS_HPP__ 2 | #define LCXX__TESTS_COMMON_KEYS_HPP__ 3 | 4 | namespace lcxx::tests::keys { 5 | 6 | constexpr auto private_key_str = R"( 7 | -----BEGIN RSA PRIVATE KEY----- 8 | MIIEowIBAAKCAQEArT6TBfYmVOrE38Y1FIwzW78Xkp8PQWCfJyjAjPWQafXznTuG 9 | 1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4ezkoS3i+s9fMmbTzLgy3MceZa+aZk 10 | CZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piSYDjdIIr+LvbGFlB+95BhsEwq35sD 11 | ap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCrlxGSGOV4cz7T59tHCXOS2SXSechm 12 | yONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa9D45xGNCUXttwhT2HaZmEpCfjEuu 13 | 1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4MajQIDAQABAoIBADaf3Eus6TktLRZB 14 | sMrhye/LuOsKLA05IfV+6/eGFhrS6hVRh5nOix80OCchlHpLo+BTWXOfSfn6Os9Z 15 | Dqu85Am3YWudLLQADsWyvZiHAEeJknXU+QXtmrrjpQp9lNGR4YuGVOo8v+Ra5qfv 16 | 5MQ5Ry3/v/la2fqE5MB0cRQt+h9T3/8P0dJm9COJGF3msX4ALzj4l2+GOxpfqTBF 17 | BlrlOc/ndEb2uUT7t4xesFeJi140E0Gl0IiFXNGd5jmk1jTGpvypkYsfqLqnxuEE 18 | 2i5CGSof2rvKS9TQvHu2ayw/HquBSzqyxYJUQl+/7BZneVl+WFwSeqfDfbzSZRX8 19 | 8B5u6QECgYEA1boG7DFCb2sVOmh0Xu7fPYwAOBaYsJpv56i/evVuUSevilB+RK/+ 20 | Gy+lvoyCsx8jG/tMOG0eWtNADV/O2FE6/jZTNgZj0Th08xMk/+2cUryxdgHsLflj 21 | bCX6CIv/mbo5H2TFPzeYnfB9wEnKUIo/W1CLi/GzuloF8wLjJ3mASxUCgYEAz4K+ 22 | Zkk1S2F7BXR9KECDu/dThKDKFe4hCJMIFNplVhwdfm/DEulZJT5TgUZoQ9SUMAfW 23 | cWCNbzXCBAHyYz3TLNBjpG1Ql4O1NwbzQPBSsGc1fPNDjBRYaVKHqj2UB8c+7+aI 24 | toPxHDTKuCvJGAW1sJfKgL5KSZ0CyfT0nRewD5kCgYAIDG1eT6yUzY+LF4vqV0yI 25 | 4NDRS+iMHgTA0JYFZ2C0Ja5yov1eUNJc67puJpR1cmK8FwaTyWgvO21aE5WSh9yU 26 | 3i7cBfmUU2/0B3CJQsV0SC7WptPiF1YrKHL2B2+ktmKYUA8thkZ1DC1wJFc+GTax 27 | laLrrjp6dhFrSVyMjALM0QKBgQDAr6ALLNl/CKKKWzPIh7eLd8qmsgNUv80OwDDV 28 | 5EIK5oqAmFjkm6e6jJhPx0gUDDYaL3zxxH0wkhN5UzF287a+uzZ7PUKDnrpLwXlp 29 | iH7P7NZfEyhaz52VFxyAeTOW0W3gqAm0qOnfjinbQFU3qD2hICHa0Ff86P3o+DuY 30 | D6HdqQKBgC+/j9dvFMoB8oE3zpA7D20Wh3qSFUf7cBD3FmBg89+qIiYvitGnTY0k 31 | CuOsl7rQhL0KAr4yfYigxgeBQXEowRVTLdhXNQBb5ux5qX0T9PpJHkoIVpwGspf+ 32 | 7BNtOAIpetC5vz4UTaNWiDxFiqg0zlT7+YCVyDMNPHyuAas687gP 33 | -----END RSA PRIVATE KEY----- 34 | )"; 35 | 36 | constexpr auto public_key_str = R"( 37 | -----BEGIN PUBLIC KEY----- 38 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArT6TBfYmVOrE38Y1FIwz 39 | W78Xkp8PQWCfJyjAjPWQafXznTuG1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4e 40 | zkoS3i+s9fMmbTzLgy3MceZa+aZkCZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piS 41 | YDjdIIr+LvbGFlB+95BhsEwq35sDap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCr 42 | lxGSGOV4cz7T59tHCXOS2SXSechmyONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa 43 | 9D45xGNCUXttwhT2HaZmEpCfjEuu1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4Ma 44 | jQIDAQAB 45 | -----END PUBLIC KEY----- 46 | )"; 47 | 48 | } // namespace lcxx::tests::keys 49 | 50 | #endif // LCXX__TESTS_COMMON_KEYS_HPP__ -------------------------------------------------------------------------------- /tests/crypto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | add_executable(crypto_test 4 | ${CMAKE_CURRENT_SOURCE_DIR}/crypto_test.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/hash_test.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/base64_test.cpp 7 | ) 8 | target_link_libraries(crypto_test 9 | GTest::gtest_main 10 | lcxx::crypto 11 | ) 12 | 13 | target_include_directories(crypto_test PRIVATE 14 | ${PROJECT_SOURCE_DIR}/common 15 | ) 16 | 17 | target_compile_definitions(crypto_test PRIVATE 18 | TEST_RESOURCE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../resources" 19 | ) 20 | 21 | include(GoogleTest) 22 | gtest_discover_tests(crypto_test) -------------------------------------------------------------------------------- /tests/crypto/base64_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | TEST( Crypto_Tests, Base64_Encode_Byte_Test ) 8 | { 9 | constexpr auto const reference = "bXkgYXJiaXRyYXJ5IHRlc3Qgc3RyaW5n"; 10 | std::vector< std::byte > const test_bytes = { 11 | std::byte{ 'm' }, std::byte{ 'y' }, std::byte{ ' ' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'b' }, 12 | std::byte{ 'i' }, std::byte{ 't' }, std::byte{ 'r' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'y' }, 13 | std::byte{ ' ' }, std::byte{ 't' }, std::byte{ 'e' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ ' ' }, 14 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'r' }, std::byte{ 'i' }, std::byte{ 'n' }, std::byte{ 'g' }, 15 | }; 16 | auto tmp = lcxx::encode::base64( { test_bytes.data(), test_bytes.size() } ); 17 | 18 | EXPECT_EQ( tmp, reference ); 19 | } 20 | 21 | TEST( Crypto_Tests, Base64_Encode_Other_Type_Test ) 22 | { 23 | std::string const reference = "bXkgYXJiaXRyYXJ5IHRlc3Qgc3RyaW5n"; 24 | std::vector< std::uint8_t > const test_bytes = { 25 | 'm', 'y', ' ', 'a', 'r', 'b', 'i', 't', 'r', 'a', 'r', 'y', 26 | ' ', 't', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g', 27 | }; 28 | auto tmp = lcxx::encode::base64( std::span< const std::uint8_t >{ test_bytes.data(), test_bytes.size() } ); 29 | 30 | EXPECT_EQ( tmp, reference ); 31 | } 32 | 33 | TEST( Crypto_Tests, Base64_Encode_Other_Type_Long_Test ) 34 | { 35 | std::string const reference = 36 | "bXkgYXJiaXRyYXJ5dmVyeSBsb25nIHRlc3Qgc3RyaW5nLGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dn" 37 | "d4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXph" 38 | "YmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZW" 39 | "ZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlq" 40 | "a2xtbm9wcXJzdHV2d3h5eg=="; 41 | std::vector< std::uint8_t > const test_bytes = { 42 | 'm', 'y', ' ', 'a', 'r', 'b', 'i', 't', 'r', 'a', 'r', 'y', 'v', 'e', 'r', 'y', ' ', 'l', 'o', 'n', 'g', ' ', 43 | 't', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g', ',', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 44 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 45 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 46 | 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 47 | 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 48 | 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 49 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 50 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 51 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 52 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 53 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 54 | 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 55 | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 56 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 57 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 58 | }; 59 | auto tmp = lcxx::encode::base64( std::span< const std::uint8_t >{ test_bytes.data(), test_bytes.size() } ); 60 | 61 | EXPECT_EQ( tmp, reference ); 62 | } 63 | 64 | TEST( Crypto_Tests, Base64_Decode_Byte_Test ) 65 | { 66 | std::string const input = "bXkgYXJiaXRyYXJ5IHRlc3Qgc3RyaW5n"; 67 | std::vector< std::byte > const reference = { 68 | std::byte{ 'm' }, std::byte{ 'y' }, std::byte{ ' ' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'b' }, 69 | std::byte{ 'i' }, std::byte{ 't' }, std::byte{ 'r' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'y' }, 70 | std::byte{ ' ' }, std::byte{ 't' }, std::byte{ 'e' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ ' ' }, 71 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'r' }, std::byte{ 'i' }, std::byte{ 'n' }, std::byte{ 'g' }, 72 | }; 73 | auto tmp = lcxx::decode::base64( input ); 74 | 75 | EXPECT_EQ( tmp, reference ); 76 | } 77 | 78 | TEST( Crypto_Tests, Base64_Decode_Byte_Long_Test ) 79 | { 80 | std::string const reference = 81 | "bXkgYXJiaXRyYXJ5dmVyeSBsb25nIHRlc3Qgc3RyaW5nLGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dn" 82 | "d4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXph" 83 | "YmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZW" 84 | "ZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlq" 85 | "a2xtbm9wcXJzdHV2d3h5eg=="; 86 | std::vector< std::byte > const test_bytes = { 87 | std::byte{ 'm' }, std::byte{ 'y' }, std::byte{ ' ' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'b' }, 88 | std::byte{ 'i' }, std::byte{ 't' }, std::byte{ 'r' }, std::byte{ 'a' }, std::byte{ 'r' }, std::byte{ 'y' }, 89 | std::byte{ 'v' }, std::byte{ 'e' }, std::byte{ 'r' }, std::byte{ 'y' }, std::byte{ ' ' }, std::byte{ 'l' }, 90 | std::byte{ 'o' }, std::byte{ 'n' }, std::byte{ 'g' }, std::byte{ ' ' }, std::byte{ 't' }, std::byte{ 'e' }, 91 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ ' ' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'r' }, 92 | std::byte{ 'i' }, std::byte{ 'n' }, std::byte{ 'g' }, std::byte{ ',' }, std::byte{ 'a' }, std::byte{ 'b' }, 93 | std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, 94 | std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, 95 | std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, 96 | std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, 97 | std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, 98 | std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, 99 | std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, 100 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, 101 | std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, 102 | std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, 103 | std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, 104 | std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, 105 | std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, 106 | std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, 107 | std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, 108 | std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, 109 | std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, 110 | std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, 111 | std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, 112 | std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, 113 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, 114 | std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, 115 | std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, 116 | std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, 117 | std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, 118 | std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, 119 | std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, 120 | std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, 121 | std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, 122 | std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, 123 | std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, 124 | std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, 125 | std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, 126 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, 127 | std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, 128 | std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, 129 | std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, 130 | std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, 131 | std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, 132 | std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, 133 | std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, 134 | std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, 135 | std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, 136 | std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, std::byte{ 'e' }, std::byte{ 'f' }, 137 | std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, std::byte{ 'k' }, std::byte{ 'l' }, 138 | std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, std::byte{ 'q' }, std::byte{ 'r' }, 139 | std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, std::byte{ 'w' }, std::byte{ 'x' }, 140 | std::byte{ 'y' }, std::byte{ 'z' }, std::byte{ 'a' }, std::byte{ 'b' }, std::byte{ 'c' }, std::byte{ 'd' }, 141 | std::byte{ 'e' }, std::byte{ 'f' }, std::byte{ 'g' }, std::byte{ 'h' }, std::byte{ 'i' }, std::byte{ 'j' }, 142 | std::byte{ 'k' }, std::byte{ 'l' }, std::byte{ 'm' }, std::byte{ 'n' }, std::byte{ 'o' }, std::byte{ 'p' }, 143 | std::byte{ 'q' }, std::byte{ 'r' }, std::byte{ 's' }, std::byte{ 't' }, std::byte{ 'u' }, std::byte{ 'v' }, 144 | std::byte{ 'w' }, std::byte{ 'x' }, std::byte{ 'y' }, std::byte{ 'z' }, 145 | }; 146 | 147 | auto tmp = lcxx::encode::base64( std::span< const std::byte >{ test_bytes.data(), test_bytes.size() } ); 148 | 149 | EXPECT_EQ( tmp, reference ); 150 | } 151 | -------------------------------------------------------------------------------- /tests/crypto/crypto_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #ifndef TEST_RESOURCE_PATH 9 | #define TEST_RESOURCE_PATH "." 10 | #endif 11 | 12 | TEST_F( key_fixture, Load_Key_String_Test ) 13 | { 14 | using path = std::filesystem::path; 15 | EXPECT_TRUE( lcxx::crypto::load_key( path( TEST_RESOURCE_PATH ) / path( "private_key.rsa" ), 16 | lcxx::crypto::key_type::private_key ) ); 17 | EXPECT_TRUE( lcxx::crypto::load_key( path( TEST_RESOURCE_PATH ) / path( "public_key" ), 18 | lcxx::crypto::key_type::public_key ) ); 19 | } 20 | 21 | TEST_F( key_fixture, Load_Key_File_Test ) 22 | { 23 | EXPECT_TRUE( private_key ); 24 | EXPECT_TRUE( public_key ); 25 | } 26 | 27 | TEST_F( key_fixture, Sign_Round_Trip_Test ) 28 | { 29 | 30 | std::string const test_string = "my arbitrary test string"; 31 | auto const signature = lcxx::crypto::sign( test_string, private_key ); 32 | auto const verified = lcxx::crypto::verify_signature( test_string, signature, public_key ); 33 | 34 | EXPECT_TRUE( verified ); 35 | } 36 | 37 | TEST_F( key_fixture, Sign_Round_Trip_Error_Test ) 38 | { 39 | std::string const test_string = "my arbitrary test string"; 40 | auto const signature = lcxx::crypto::sign( test_string, private_key ); 41 | auto const verified = lcxx::crypto::verify_signature( std::string( "_" ) + test_string, signature, public_key ); 42 | 43 | EXPECT_FALSE( verified ); 44 | } 45 | 46 | std::string private_key_str = R"( 47 | -----BEGIN RSA PRIVATE KEY----- 48 | MIIEowIBAAKCAQEArT6TBfYmVOrE38Y1FIwzW78Xkp8PQWCfJyjAjPWQafXznTuG 49 | 1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4ezkoS3i+s9fMmbTzLgy3MceZa+aZk 50 | CZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piSYDjdIIr+LvbGFlB+95BhsEwq35sD 51 | ap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCrlxGSGOV4cz7T59tHCXOS2SXSechm 52 | yONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa9D45xGNCUXttwhT2HaZmEpCfjEuu 53 | 1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4MajQIDAQABAoIBADaf3Eus6TktLRZB 54 | sMrhye/LuOsKLA05IfV+6/eGFhrS6hVRh5nOix80OCchlHpLo+BTWXOfSfn6Os9Z 55 | Dqu85Am3YWudLLQADsWyvZiHAEeJknXU+QXtmrrjpQp9lNGR4YuGVOo8v+Ra5qfv 56 | 5MQ5Ry3/v/la2fqE5MB0cRQt+h9T3/8P0dJm9COJGF3msX4ALzj4l2+GOxpfqTBF 57 | BlrlOc/ndEb2uUT7t4xesFeJi140E0Gl0IiFXNGd5jmk1jTGpvypkYsfqLqnxuEE 58 | 2i5CGSof2rvKS9TQvHu2ayw/HquBSzqyxYJUQl+/7BZneVl+WFwSeqfDfbzSZRX8 59 | 8B5u6QECgYEA1boG7DFCb2sVOmh0Xu7fPYwAOBaYsJpv56i/evVuUSevilB+RK/+ 60 | Gy+lvoyCsx8jG/tMOG0eWtNADV/O2FE6/jZTNgZj0Th08xMk/+2cUryxdgHsLflj 61 | bCX6CIv/mbo5H2TFPzeYnfB9wEnKUIo/W1CLi/GzuloF8wLjJ3mASxUCgYEAz4K+ 62 | Zkk1S2F7BXR9KECDu/dThKDKFe4hCJMIFNplVhwdfm/DEulZJT5TgUZoQ9SUMAfW 63 | cWCNbzXCBAHyYz3TLNBjpG1Ql4O1NwbzQPBSsGc1fPNDjBRYaVKHqj2UB8c+7+aI 64 | toPxHDTKuCvJGAW1sJfKgL5KSZ0CyfT0nRewD5kCgYAIDG1eT6yUzY+LF4vqV0yI 65 | 4NDRS+iMHgTA0JYFZ2C0Ja5yov1eUNJc67puJpR1cmK8FwaTyWgvO21aE5WSh9yU 66 | 3i7cBfmUU2/0B3CJQsV0SC7WptPiF1YrKHL2B2+ktmKYUA8thkZ1DC1wJFc+GTax 67 | laLrrjp6dhFrSVyMjALM0QKBgQDAr6ALLNl/CKKKWzPIh7eLd8qmsgNUv80OwDDV 68 | 5EIK5oqAmFjkm6e6jJhPx0gUDDYaL3zxxH0wkhN5UzF287a+uzZ7PUKDnrpLwXlp 69 | iH7P7NZfEyhaz52VFxyAeTOW0W3gqAm0qOnfjinbQFU3qD2hICHa0Ff86P3o+DuY 70 | D6HdqQKBgC+/j9dvFMoB8oE3zpA7D20Wh3qSFUf7cBD3FmBg89+qIiYvitGnTY0k 71 | CuOsl7rQhL0KAr4yfYigxgeBQXEowRVTLdhXNQBb5ux5qX0T9PpJHkoIVpwGspf+ 72 | 7BNtOAIpetC5vz4UTaNWiDxFiqg0zlT7+YCVyDMNPHyuAas687gP 73 | -----END RSA PRIVATE KEY----- 74 | )"; 75 | 76 | std::string public_key_str = R"( 77 | -----BEGIN PUBLIC KEY----- 78 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArT6TBfYmVOrE38Y1FIwz 79 | W78Xkp8PQWCfJyjAjPWQafXznTuG1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4e 80 | zkoS3i+s9fMmbTzLgy3MceZa+aZkCZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piS 81 | YDjdIIr+LvbGFlB+95BhsEwq35sDap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCr 82 | lxGSGOV4cz7T59tHCXOS2SXSechmyONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa 83 | 9D45xGNCUXttwhT2HaZmEpCfjEuu1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4Ma 84 | jQIDAQAB 85 | -----END PUBLIC KEY----- 86 | )"; 87 | -------------------------------------------------------------------------------- /tests/crypto/hash_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | template < std::ranges::range R > 10 | auto str_to_vec( std::string_view const str ) -> R 11 | requires( sizeof( std::ranges::range_value_t< R > ) == 1 ) 12 | { 13 | auto bytes = str | std::views::transform( []( auto && c ) { 14 | return *reinterpret_cast< std::ranges::range_value_t< R > const * >( &c ); 15 | } ); 16 | return { bytes.begin(), bytes.end() }; 17 | } 18 | 19 | template < typename T, std::size_t N > 20 | auto gen_byte_arr( std::initializer_list< T > const & r ) -> std::array< std::byte, N > 21 | { 22 | auto bytes = r | std::views::transform( []( auto && c ) { return std::byte{ c }; } ); 23 | 24 | std::array< std::byte, N > arr; 25 | std::ranges::copy( bytes, arr.begin() ); 26 | return arr; 27 | } 28 | 29 | constexpr auto test_string = "my arbitrary test string"; 30 | static const auto reference_hash = gen_byte_arr< uint8_t, 64 >( { 31 | 0xb2, 0xd1, 0xdc, 0x2a, 0xd5, 0x61, 0xc7, 0x2b, 0x68, 0x53, 0x6c, 0xa6, 0x34, 0x5b, 0x2e, 0xd2, 32 | 0xeb, 0xba, 0x6e, 0x97, 0x2a, 0x0b, 0x71, 0x6c, 0x03, 0xdc, 0x44, 0xc7, 0xaa, 0xcd, 0x02, 0xa7, 33 | 0x89, 0x9e, 0xc1, 0x7a, 0xca, 0x2a, 0x3d, 0xeb, 0xc2, 0xa5, 0xd5, 0x70, 0xdb, 0xeb, 0x8e, 0x9c, 34 | 0xd6, 0xbf, 0xa4, 0xe5, 0x19, 0xe5, 0xd0, 0xe6, 0x56, 0xda, 0x06, 0x62, 0xc3, 0x0b, 0xc8, 0x2b, 35 | } ); 36 | 37 | TEST( Crypto_Tests, SHA512_Hash_String_Test ) 38 | { 39 | auto [sha512, err] = lcxx::hash::sha512( test_string ); 40 | EXPECT_EQ( err, lcxx::hash::error::ok ); 41 | EXPECT_EQ( sha512, reference_hash ); 42 | } 43 | 44 | TEST( Crypto_Tests, SHA512_Hash_Byte_Test ) 45 | { 46 | auto const test_bytes = str_to_vec< std::vector< std::byte > >( test_string ); 47 | auto [sha512, err] = lcxx::hash::sha512( test_bytes ); 48 | EXPECT_EQ( err, lcxx::hash::error::ok ); 49 | EXPECT_EQ( sha512, reference_hash ); 50 | } 51 | 52 | TEST( Crypto_Tests, SHA512_Hash_Other_Type_Test ) 53 | { 54 | auto const test_bytes = str_to_vec< std::vector< uint8_t > >( test_string ); 55 | auto [sha512, err] = lcxx::hash::sha512( std::span< const uint8_t >{ test_bytes.data(), test_bytes.size() } ); 56 | EXPECT_EQ( err, lcxx::hash::error::ok ); 57 | EXPECT_EQ( sha512, reference_hash ); 58 | } 59 | 60 | TEST( Crypto_Tests, SHA512_Hash_Test_whitespace ) 61 | { 62 | constexpr auto test_string = " "; 63 | auto reference_hash = gen_byte_arr< uint8_t, 64 >( { 64 | 0xf9, 0x0d, 0xdd, 0x77, 0xe4, 0x00, 0xdf, 0xe6, 0xa3, 0xfc, 0xf4, 0x79, 0xb0, 0x0b, 0x1e, 0xe2, 65 | 0x9e, 0x70, 0x15, 0xc5, 0xbb, 0x8c, 0xd7, 0x0f, 0x5f, 0x15, 0xb4, 0x88, 0x6c, 0xc3, 0x39, 0x27, 66 | 0x5f, 0xf5, 0x53, 0xfc, 0x8a, 0x05, 0x3f, 0x8d, 0xdc, 0x73, 0x24, 0xf4, 0x51, 0x68, 0xcf, 0xfa, 67 | 0xf8, 0x1f, 0x8c, 0x3a, 0xc9, 0x39, 0x96, 0xf6, 0x53, 0x6e, 0xef, 0x38, 0xe5, 0xe4, 0x07, 0x68, 68 | } ); 69 | 70 | auto [sha512, err] = lcxx::hash::sha512( test_string ); 71 | EXPECT_EQ( err, lcxx::hash::error::ok ); 72 | EXPECT_EQ( sha512, reference_hash ); 73 | } -------------------------------------------------------------------------------- /tests/identifiers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | add_executable(identifiers_test 4 | ${CMAKE_CURRENT_SOURCE_DIR}/identifiers_test.cpp 5 | ) 6 | target_link_libraries(identifiers_test 7 | GTest::gtest_main 8 | lcxx::identifiers 9 | ) 10 | 11 | include(GoogleTest) 12 | gtest_discover_tests(identifiers_test) -------------------------------------------------------------------------------- /tests/identifiers/identifiers_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using namespace lcxx::experimental::identifiers; 7 | using hws = hw_ident_strat; 8 | using oss = os_ident_strat; 9 | 10 | template < typename Strat_Enum > 11 | requires( std::is_enum_v< Strat_Enum > ) void full_ident_test( Strat_Enum strat ) 12 | { 13 | std::string hash; 14 | if constexpr ( std::is_same< hw_ident_strat, Strat_Enum >() ) { 15 | hash = hardware( strat ).hash; 16 | } 17 | else if constexpr ( std::is_same< os_ident_strat, Strat_Enum >() ) { 18 | hash = os( strat ).hash; 19 | } 20 | 21 | EXPECT_TRUE( verify( strat, hash ) ); 22 | } 23 | 24 | TEST( Identifier_Tests, Hardware_Ident_All ) { full_ident_test( hws::all ); } 25 | TEST( Identifier_Tests, Hardware_Ident_cpu ) { full_ident_test( hws::cpu ); } 26 | TEST( Identifier_Tests, Hardware_Ident_cpu_n_cores ) { full_ident_test( hws::cpu_n_cores ); } 27 | TEST( Identifier_Tests, Hardware_Ident_cpu_n_threads ) { full_ident_test( hws::cpu_n_threads ); } 28 | TEST( Identifier_Tests, Hardware_Ident_cpu_max_frequency ) { full_ident_test( hws::cpu_max_frequency ); } 29 | TEST( Identifier_Tests, Hardware_Ident_cpu_vendor ) { full_ident_test( hws::cpu_vendor ); } 30 | TEST( Identifier_Tests, Hardware_Ident_cpu_model_name ) { full_ident_test( hws::cpu_model_name ); } 31 | 32 | TEST( Identifier_Tests, Hardware_Ident_Combined0 ) { full_ident_test( hws::cpu_vendor | hws::cpu_max_frequency ); } 33 | TEST( Identifier_Tests, Hardware_Ident_Combined1 ) 34 | { 35 | full_ident_test( hws::cpu_n_cores | hws::cpu_n_threads | hws::cpu_vendor ); 36 | } 37 | 38 | TEST( Identifier_Tests, OS_Ident_All ) { full_ident_test( oss::all ); } 39 | TEST( Identifier_Tests, OS_Ident_os ) { full_ident_test( oss::os ); } 40 | TEST( Identifier_Tests, OS_Ident_os_name ) { full_ident_test( oss::os_name ); } 41 | TEST( Identifier_Tests, OS_Ident_os_architecture ) { full_ident_test( oss::os_architecture ); } 42 | TEST( Identifier_Tests, OS_Ident_os_pc_name ) { full_ident_test( oss::os_pc_name ); } 43 | TEST( Identifier_Tests, OS_Ident_user ) { full_ident_test( oss::user ); } 44 | TEST( Identifier_Tests, OS_Ident_user_name ) { full_ident_test( oss::user_name ); } 45 | TEST( Identifier_Tests, OS_Ident_user_uid ) { full_ident_test( oss::user_uid ); } 46 | TEST( Identifier_Tests, OS_Ident_user_groups ) { full_ident_test( oss::user_groups ); } 47 | TEST( Identifier_Tests, OS_Ident_user_gids ) { full_ident_test( oss::user_gids ); } 48 | 49 | TEST( Identifier_Tests, OS_Ident_Combined0 ) { full_ident_test( oss::os_pc_name | oss::user_uid | oss::user_gids ); } 50 | TEST( Identifier_Tests, OS_Ident_Combined1 ) { full_ident_test( oss::os | oss::user_uid | oss::user_groups ); } 51 | TEST( Identifier_Tests, OS_Ident_Combined2 ) { full_ident_test( oss::os_name | oss::os_pc_name | oss::user ); } -------------------------------------------------------------------------------- /tests/lcxx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | add_executable(lcxx_test 4 | ${CMAKE_CURRENT_SOURCE_DIR}/lcxx_test.cpp 5 | ) 6 | target_link_libraries(lcxx_test 7 | GTest::gtest_main 8 | lcxx::lcxx 9 | ) 10 | 11 | target_include_directories(lcxx_test PRIVATE 12 | ${PROJECT_SOURCE_DIR}/common 13 | ) 14 | 15 | include(GoogleTest) 16 | gtest_discover_tests(lcxx_test) -------------------------------------------------------------------------------- /tests/lcxx/lcxx_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | auto license_factory() -> lcxx::license 10 | { 11 | 12 | lcxx::license lic; 13 | lic.push_content( "some key", "some value" ); 14 | lic.push_content( "1", "1" ); 15 | lic.push_content( "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345" 16 | "678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901" 17 | "234567890", 18 | "" ); 19 | return lic; 20 | } 21 | 22 | TEST_F( key_fixture, License_Json_Round_Trip_Verify ) 23 | { 24 | auto lic = license_factory(); 25 | 26 | auto [lic_restored, signature] = lcxx::from_json( lcxx::to_json( lic, private_key ) ); 27 | 28 | EXPECT_TRUE( lcxx::verify_license( lic_restored, signature, public_key ) ); 29 | } 30 | 31 | TEST_F( key_fixture, License_Round_Trip_Verify ) 32 | { 33 | auto const lic = license_factory(); 34 | auto const signature = lcxx::sign( lic, private_key ); 35 | 36 | EXPECT_TRUE( verify_license( lic, signature, public_key ) ); 37 | } -------------------------------------------------------------------------------- /tests/resources/private_key.rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArT6TBfYmVOrE38Y1FIwzW78Xkp8PQWCfJyjAjPWQafXznTuG 3 | 1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4ezkoS3i+s9fMmbTzLgy3MceZa+aZk 4 | CZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piSYDjdIIr+LvbGFlB+95BhsEwq35sD 5 | ap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCrlxGSGOV4cz7T59tHCXOS2SXSechm 6 | yONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa9D45xGNCUXttwhT2HaZmEpCfjEuu 7 | 1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4MajQIDAQABAoIBADaf3Eus6TktLRZB 8 | sMrhye/LuOsKLA05IfV+6/eGFhrS6hVRh5nOix80OCchlHpLo+BTWXOfSfn6Os9Z 9 | Dqu85Am3YWudLLQADsWyvZiHAEeJknXU+QXtmrrjpQp9lNGR4YuGVOo8v+Ra5qfv 10 | 5MQ5Ry3/v/la2fqE5MB0cRQt+h9T3/8P0dJm9COJGF3msX4ALzj4l2+GOxpfqTBF 11 | BlrlOc/ndEb2uUT7t4xesFeJi140E0Gl0IiFXNGd5jmk1jTGpvypkYsfqLqnxuEE 12 | 2i5CGSof2rvKS9TQvHu2ayw/HquBSzqyxYJUQl+/7BZneVl+WFwSeqfDfbzSZRX8 13 | 8B5u6QECgYEA1boG7DFCb2sVOmh0Xu7fPYwAOBaYsJpv56i/evVuUSevilB+RK/+ 14 | Gy+lvoyCsx8jG/tMOG0eWtNADV/O2FE6/jZTNgZj0Th08xMk/+2cUryxdgHsLflj 15 | bCX6CIv/mbo5H2TFPzeYnfB9wEnKUIo/W1CLi/GzuloF8wLjJ3mASxUCgYEAz4K+ 16 | Zkk1S2F7BXR9KECDu/dThKDKFe4hCJMIFNplVhwdfm/DEulZJT5TgUZoQ9SUMAfW 17 | cWCNbzXCBAHyYz3TLNBjpG1Ql4O1NwbzQPBSsGc1fPNDjBRYaVKHqj2UB8c+7+aI 18 | toPxHDTKuCvJGAW1sJfKgL5KSZ0CyfT0nRewD5kCgYAIDG1eT6yUzY+LF4vqV0yI 19 | 4NDRS+iMHgTA0JYFZ2C0Ja5yov1eUNJc67puJpR1cmK8FwaTyWgvO21aE5WSh9yU 20 | 3i7cBfmUU2/0B3CJQsV0SC7WptPiF1YrKHL2B2+ktmKYUA8thkZ1DC1wJFc+GTax 21 | laLrrjp6dhFrSVyMjALM0QKBgQDAr6ALLNl/CKKKWzPIh7eLd8qmsgNUv80OwDDV 22 | 5EIK5oqAmFjkm6e6jJhPx0gUDDYaL3zxxH0wkhN5UzF287a+uzZ7PUKDnrpLwXlp 23 | iH7P7NZfEyhaz52VFxyAeTOW0W3gqAm0qOnfjinbQFU3qD2hICHa0Ff86P3o+DuY 24 | D6HdqQKBgC+/j9dvFMoB8oE3zpA7D20Wh3qSFUf7cBD3FmBg89+qIiYvitGnTY0k 25 | CuOsl7rQhL0KAr4yfYigxgeBQXEowRVTLdhXNQBb5ux5qX0T9PpJHkoIVpwGspf+ 26 | 7BNtOAIpetC5vz4UTaNWiDxFiqg0zlT7+YCVyDMNPHyuAas687gP 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/resources/public_key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArT6TBfYmVOrE38Y1FIwz 3 | W78Xkp8PQWCfJyjAjPWQafXznTuG1JEoT0eMlDC1HwXTJwohldzirePphYxp6I4e 4 | zkoS3i+s9fMmbTzLgy3MceZa+aZkCZi8adQxVV2mZPn+LxNU8aZVDLh67lcs8piS 5 | YDjdIIr+LvbGFlB+95BhsEwq35sDap6pYvo+9WlxcGxbDRZLViFlUIEgAOLmIqCr 6 | lxGSGOV4cz7T59tHCXOS2SXSechmyONB8fMAVFBYUhBlJT80syZVb/SqD+7EIGPa 7 | 9D45xGNCUXttwhT2HaZmEpCfjEuu1VQRaWZZnY+jpMQ1XYTQQifoapj3WTGJo4Ma 8 | jQIDAQAB 9 | -----END PUBLIC KEY----- 10 | --------------------------------------------------------------------------------