
License C++ library
v2.0.0
Table of contents
- Overview
- Versions
- Library files
- License class description
- FIPS 140-3 compliance
- EU Cyber Resilience Act compliance
- Usage
- Build and connect to your project
- Example
Overview
License is a C++ library for offline, time-limited application licensing. It generates a token from a computer identifier, issues a license for that token, and verifies the license’s signature, expiry and module permissions. The vendor creates a key pair once: the private key stays with the vendor, and the public key is compiled into the application. Possessing the public key does not enable license forgery, and changing the signed computer, expiry or module data invalidates the signature. Hex letter case does not affect verification, and the license text is not a unique identifier for an entitlement. A license can name several functional modules, for example tracking and detector, so one key pair and one license file can cover a product. ECDSA P-256 with SHA-256 is provided by OpenSSL 3.x (linked, Apache 2.0 license). The library prefers a compatible, correctly configured FIPS provider and otherwise uses the default provider; an invalid configuration file causes initialization to fail. Provider selection alone does not establish FIPS validation. The public interface uses standard C++ types and static methods. The library requires C++17 and implements computer identification on Linux and Windows. Offline checks depend on the local clock and identifiers and cannot prevent clock rollback, cloned installations or application patching. For the complete workflow, see Usage.
Versions
Table 1 - Library versions.
| Version | Release date | What’s new |
|---|---|---|
| 1.0.0 | 31.01.2022 | First version. |
| 1.1.0 | 28.09.2023 | - Hardware dependency moved to MAC address from CPU ID. - Documentation updated. |
| 1.1.1 | 05.01.2024 | - Code style issues fixed. - Documentation updated. - Only AES256 algorithm is used. |
| 1.1.2 | 15.04.2024 | - Documentation updated. |
| 1.1.3 | 17.05.2024 | - Documentation updated. |
| 1.1.4 | 19.07.2024 | - CMake updated. - Repository structure updated. |
| 1.2.0 | 19.07.2024 | - Custom key support added instead of only MAC address. |
| 1.2.1 | 12.12.2025 | - Enumerate MAC interfaces for default key and validate licenses against all detected addresses. |
| 2.0.0 | 09.09.2026 | - Licensing moved from a shared secret to ECDSA P-256 with SHA-256, signed by a vendor private key and checked with a public key compiled into the application. - OpenSSL provides cryptography, preferring a compatible, configured FIPS provider at run time. - Tokens use OS or hardware identifiers; software-only network interfaces are filtered, but cloned identifiers are not detected. - Expiry uses UTC seconds rather than local time. - Cryptographic known-answer self-tests, hardened build flags and a test suite. - Breaking change: licenses issued by 1.x are not accepted and must be reissued. |
Library files
The library is supplied as source code in a CMake project.
License.h and the generated LicenseVersion.h are the only headers a consuming application needs. CMake generates LicenseVersion.h in the library’s build directory (build/src in a standalone build), not in the source tree. Linking the License CMake target provides both include directories and its dependencies. When packaging headers manually, include src/License.h and the generated header from the matching build. Everything under impl is implementation detail. The repository structure is shown below:
CMakeLists.txt ---------------- Main CMake file of the library.
static ------------------------ Folder with documentation images.
src --------------------------- Folder with library source code.
CMakeLists.txt ------------ Library CMake file.
License.h ----------------- PUBLIC header: the whole interface.
License.cpp --------------- C++ implementation file.
LicenseVersion.h.in ------- Generates the PUBLIC LicenseVersion.h header.
impl ---------------------- Internal implementation.
Crypto.h -------------- OpenSSL services and provider selection.
Crypto.cpp ------------ Cryptography implementation file.
LicenseFormat.h ------- Licence record layout and hex coding.
LicenseFormat.cpp ----- Licence format implementation file.
Fingerprint.h --------- Hardware identification.
Fingerprint.cpp ------- Hardware identification implementation.
tests ------------------------- Folder with tests.
CMakeLists.txt ------------ Tests CMake file.
TestHarness.h ------------- Assertion helpers.
test_crypto.cpp ----------- Cryptographic primitive tests.
test_format.cpp ----------- Licence encoding tests.
test_license.cpp ---------- End-to-end issue and check tests.
test_regressions.cpp ------ Untrusted input and robustness tests.
test_examples.cmake ------ End-to-end command-line example tests.
run_fips_tests.sh --------- Helper to run the suite against a FIPS module.
examples ---------------------- Folder with examples.
CMakeLists.txt ------------ CMake file for examples.
KeyGenerator -------------- Folder with vendor key generator.
CMakeLists.txt -------- Application CMake file.
main.cpp -------------- Application source code file.
TokenGenerator ------------ Folder with token generator.
CMakeLists.txt -------- Application CMake file.
main.cpp -------------- Application source code file.
LicenseGenerator ---------- Folder with license generator.
CMakeLists.txt -------- Application CMake file.
main.cpp -------------- Application source code file.
LicenseTest --------------- Folder with license validator.
CMakeLists.txt -------- Application CMake file.
main.cpp -------------- Application source code file.
License class description
License class declaration
The License interface class is declared in License.h. Every method is static and every parameter is a standard C++ type. Class declaration:
namespace cr
{
namespace utils
{
/// License class.
class License
{
public:
/// Get License class version.
static std::string getVersion();
/// Generate a vendor key pair.
static bool generateKeys(std::string &privateKey,
std::string &publicKey);
/// Generate the token of the computer this runs on.
static std::string getToken();
/// Generate a license string from a token.
static bool getLicense(const std::string &token,
const std::string &modules, int days,
const std::string &privateKey,
std::string &license);
/// Check whether a license unlocks one module.
static bool checkLicense(const std::string &license,
const std::string &moduleId,
const std::string &publicKey);
/// Get the number of days a license still has to run.
static bool getLicenseDaysLeft(const std::string &license,
const std::string &publicKey,
int &daysLeft);
/// List the modules a license unlocks.
static bool getLicenseModules(const std::string &license,
const std::string &publicKey,
std::string &modules);
/// Get the name of the cryptographic backend in use.
static std::string getCryptoBackend();
/// Report whether the FIPS provider was selected at initialization.
static bool isFipsMode();
/// Run the cryptographic known-answer self-tests.
static bool selfTest();
/// Point the library at an OpenSSL configuration file.
static bool setCryptoConfig(const std::string &configPath,
const std::string &moduleDir = "");
};
}
}
getVersion method
The getVersion() method returns the library version string without initializing cryptography. Method declaration:
static std::string getVersion();
The method can be used without a License instance:
std::cout << "License class version: " << cr::utils::License::getVersion();
Console output:
License class version: 2.0.0
generateKeys method
The generateKeys(…) method creates a vendor key pair. Run it once, on a computer you trust, and use a separate key pair for each product: that is what keeps a license for one product from being accepted by another. Method declaration:
static bool generateKeys(std::string &privateKey, std::string &publicKey);
| Parameter | Value |
|---|---|
| privateKey | Reference to store the private key, 64 lowercase hex characters. Secret. Never ship it and never commit it. |
| publicKey | Reference to store the public key, 130 lowercase hex characters. Safe to publish and to compile into the application. |
Returns: TRUE if the key pair is generated or FALSE if not.
getToken method
The getToken() method generates a computer token that the customer sends to the vendor. It hashes the first available identifier: the OS installation ID, a hardware-backed network adapter address, or a readable firmware UUID on Linux. Software-only network interfaces are filtered, but virtual hardware may still be reported as hardware-backed. This is not hardware attestation: cloned installations and machines exposing the same identifier can have the same token. Method declaration:
static std::string getToken();
Returns: a token of 32 lowercase hex characters, or an empty string when no identifier is available or cryptography fails.
getLicense method
The getLicense(…) method generates a license from a token string, a list of modules and a number of days. The resulting string may be written to any file. One license can unlock several modules at once, so a customer who buys three modules still receives a single file. This is the vendor side and needs the private key. Method declaration:
static bool getLicense(const std::string &token,
const std::string &modules, int days,
const std::string &privateKey,
std::string &license);
| Parameter | Value |
|---|---|
| token | Token string received from the customer. |
| modules | Module name or comma-separated list, for example tracking,detector. Names use ASCII letters, digits, _, - and ., with at most 32 characters each. Case is ignored and surrounding spaces and tabs are trimmed. The sorted, deduplicated list, including commas, must fit in 255 bytes. |
| days | Validity period from 1 to 36500 days, each 86400 seconds, starting at issuance rather than first use. |
| privateKey | Vendor private key from generateKeys(). |
| license | Reference to store the uppercase hex license string; cleared on failure. |
Returns: TRUE if a license is created or FALSE if not.
checkLicense method
The checkLicense(…) method checks whether a license unlocks one module. It verifies the signature, then that the license was issued for this computer, has not expired, and names the module asked about. Call it once per module, at the place where that module is used. Method declaration:
static bool checkLicense(const std::string &license,
const std::string &moduleId,
const std::string &publicKey);
| Parameter | Value |
|---|---|
| license | License string read from your file. Hex characters only: strip any trailing newline or carriage return a text file adds. |
| moduleId | Module to ask about, for example tracking. Case-insensitive. Exactly one name; a list is not accepted here. |
| publicKey | Vendor public key compiled into the application. |
Returns: TRUE if license is valid for this computer, not expired and unlocks that module, or FALSE.
getLicenseDaysLeft method
The getLicenseDaysLeft(…) method extracts the remaining validity from a license, so an application can warn a customer before it runs out. It checks the signature but applies no computer or expiry rule, so it still reports a value for a license that has expired or belongs to another machine. Method declaration:
static bool getLicenseDaysLeft(const std::string &license,
const std::string &publicKey,
int &daysLeft);
| Parameter | Value |
|---|---|
| license | License string read from your file. |
| publicKey | Vendor public key compiled into the application. |
| daysLeft | Remaining whole days, rounded down and clamped to [-36500, 36500]. It reads 0 in the final partial day and -1 in the first day after expiry. Reset to 0 on failure; use checkLicense() to decide validity, including at the exact expiry time. |
Returns: TRUE if the signature is valid and the information is extracted or FALSE if not.
getLicenseModules method
The getLicenseModules(…) method lists the modules a license unlocks, which is useful in an “About” dialog and in support correspondence. Only the signature is checked, so it also reports the modules of a license that has expired or belongs to another computer. Method declaration:
static bool getLicenseModules(const std::string &license,
const std::string &publicKey,
std::string &modules);
| Parameter | Value |
|---|---|
| license | License string read from your file. |
| publicKey | Vendor public key compiled into the application. |
| modules | Reference to store the comma-separated list, in lowercase and sorted. |
Returns: TRUE if the signature is valid and the list is extracted or FALSE if not.
getCryptoBackend method
The getCryptoBackend() method returns the name of the cryptographic backend selected at run time. Method declaration:
static std::string getCryptoBackend();
Returns: a backend name, for example openssl-3.0.13 (fips provider), or unavailable if initialization failed. The version identifies the OpenSSL library, not necessarily the separately installed FIPS module.
isFipsMode method
The isFipsMode() method reports whether initialization loaded the FIPS provider and enabled the fips=yes algorithm property. It does not check a CMVP certificate or continuously monitor provider health. Log it at startup and require it, along with successful cryptographic operations, when the deployment requires FIPS. The module version, platform and configuration must separately satisfy the applicable security policy. Method declaration:
static bool isFipsMode();
Returns: TRUE when FIPS was selected at initialization, or FALSE otherwise.
selfTest method
The selfTest() method checks SHA-256 and ECDSA signature verification against fixed known answers once per process. Later calls return the cached result; license operations also consult it. Calling this at startup checks the selected backend before a license is available. These application-level checks do not replace the provider’s own self-tests. Method declaration:
static bool selfTest();
Returns: TRUE if every self-test passed or FALSE if not.
setCryptoConfig method
The setCryptoConfig(…) method records an OpenSSL configuration file and an optional provider directory. Call it before any method except getVersion(), because the backend is selected once on first use. Parsing is deferred: after this call succeeds, check selfTest() and, when required, isFipsMode().
The private context’s configuration filename is not selected by OPENSSL_CONF, and its provider search directory is pinned rather than selected by OPENSSL_MODULES. This is not isolation from the entire process environment: OpenSSL configurations can contain environment substitutions, includes and explicit module paths, and the host process has its own OpenSSL initialization and dynamic-loader behavior. Protect the configuration, included files, provider modules and their parent directories. Avoid environment-dependent or relative paths in security-sensitive configuration. Method declaration:
static bool setCryptoConfig(const std::string &configPath,
const std::string &moduleDir = "");
| Parameter | Value |
|---|---|
| configPath | Readable regular OpenSSL configuration file. For FIPS, include the installed module’s integrity configuration. |
| moduleDir | Existing, trusted provider directory. Empty uses the directory compiled into OpenSSL. Prefer absolute paths and verify directory permissions. |
Returns: TRUE if the settings were recorded; FALSE for invalid paths, an unreadable configuration file, or an already selected backend. TRUE does not mean the file parsed successfully or that FIPS is available. An initialization error makes cryptographic operations fail.
FIPS 140-3 compliance
This library has no FIPS validation certificate. It consumes OpenSSL cryptographic services outside the provider’s cryptographic boundary. A compatible FIPS provider is preferred when available and configured; the default provider is not a substitute for a validated deployment. FIPS 140-3 claims depend on the actual module and operating environment.
First, approved algorithms alone do not establish module validation. The NIST/CCCS CMVP validates specific modules and configurations. An independent implementation could undergo validation, but this project instead uses OpenSSL for hashing and signatures. Merely building OpenSSL with FIPS support does not establish validation, and a FIPS 140-2 certificate is not a FIPS 140-3 certificate.
Second, license checking can consume approved cryptographic services, but the deployed module’s security policy, key handling, entropy requirements and supported operating environments still apply. Consult the OpenSSL FIPS provider documentation and the security policy associated with the exact certificate.
What you may state, and how:
- Identify the deployed FIPS module, its version and CMVP certificate, including the standard it was validated against. Confirm its current status and the applicability of its security policy to your platform and configuration.
- Declare in writing that
cr::utils::Licenselies outside the cryptographic boundary and is a consumer of the module’s signature and message-digest services. - The library uses ECDSA P-256 key generation, signing and verification, key validation and SHA-256, with provider-managed random generation as needed. Raw private-key input and output also exist outside the cryptographic boundary and require protection.
- Log the backend and
isFipsMode()at startup, requireselfTest()to succeed, and handle each operation’s failure. These checks are useful operational evidence, not proof of certification or product compliance. The FIPS test helper requires an installed module and fails if the test processes fall back to the default provider.
EU Cyber Resilience Act compliance
The Cyber Resilience Act, Regulation (EU) 2024/2847, sets cybersecurity requirements for products with digital elements made available on the EU market within its scope. Most requirements apply from 11 December 2027. Article 14 reporting obligations apply from 11 September 2026, including to in-scope products placed on the market earlier under Article 69(3). Chapter IV on conformity assessment bodies applies from 11 June 2026. Scope, exclusions and obligations depend on the product and the economic operator.
CE marking and the EU declaration of conformity concern the product placed on the market; a separately marketed software component can itself be in scope. Article 13(5) requires due diligence over integrated components. The following implementation measures may support a product’s assessment, but neither this table nor passing tests establishes CRA conformity:
Table 2 - Contribution to the essential requirements of Annex I.
| Requirement | How it is addressed |
|---|---|
| Part I (1), risk-appropriate security | Licenses are verified with a public key, so producing one requires the vendor’s private key. |
| Part I 2(a), no known exploitable vulnerabilities | The parser bounds decoded license lengths and module lists. Regression tests and optional ASan/UBSan builds support testing; dependency monitoring, security review and vulnerability handling are still required. |
| Part I 2(d), protection from unauthorised access | No secret key material is embedded in a shipped binary; the private key is supplied by the vendor at issuing time only. |
| Part I 2(f), integrity | A signature covers the computer digest, expiry and modules with a domain tag. The tag separates protocols with different tags, but does not prevent replay of an existing license or use across products sharing a key and format. |
| Part I 2(g), data minimisation | Tokens and licenses contain a digest rather than the raw identifier. This is not anonymisation: low-entropy identifiers can be recovered by search, and tokens can enable correlation. Treat them as potentially personal identifiers. |
| Part I 2(j), limited attack surface | The library does not invoke a shell and pins its provider search directory. Trusted configuration contents, includes, explicit provider paths and the host process environment remain part of the deployment’s trust boundary. |
| Part I 2(k), exploitation mitigation | GCC/Clang builds enable stack protection; Linux ELF links also request full RELRO and a non-executable stack. MSVC builds request /GS, /sdl and /guard:cf, plus linker mitigations. Verify the final shipped binaries. |
| Part II, vulnerability handling | Not covered by the library. Publish a coordinated vulnerability disclosure policy, a security contact, a software bill of materials and a record of fixed vulnerabilities for your product. |
Your own product still needs its own risk assessment, technical documentation under Annex VII, an EU declaration of conformity under Annex V, CE marking, a conformity assessment route under Article 32, a declared support period, a security-update mechanism, a software bill of materials, and the Article 14 reporting process.
Usage
This section explains, step by step, everything that has to happen to license an application. Two different people are involved and it is important not to confuse them:
- You, the vendor. You build the application and you create licenses. You own a secret signing key.
- Your customer, the user. They run the application and receive a license from you. They never see your secret key.
The whole scheme rests on one idea: the secret key creates licenses, and the key inside your application only checks them. Checking cannot create. That is why it is safe to ship the checking key inside your application, and why a customer cannot make their own license or edit the one you sent them.
Before you start, build the library and its example programs as described in Build and connect to your project. Single-configuration builds place them in build/bin; multi-configuration generators usually add a configuration directory such as Release. KeyGenerator and LicenseGenerator handle the private key and belong on the vendor’s trusted system. TokenGenerator prints a token. LicenseTest demonstrates validation, but reads public.key from a file; a production application must anchor trust in its own embedded or otherwise protected public key. File paths in the examples are relative to the working directory. Run vendor tools in an access-restricted directory, not a shared download or temporary directory.
Step 1. Create your key pair, once
Do this once, on a computer you trust. You will not repeat it for each customer.
./KeyGenerator
It creates private.key and public.key without overwriting existing files, and does not print the private key. Keep private.key secret: never ship or commit it, and back it up securely. On Linux the new private file is restricted to its owner; on Windows prepare a directory with an appropriate ACL. If the key leaks, replace the trusted public key in the product and reissue licenses under a new key pair; existing binaries trusting the old key remain vulnerable. For non-exportable HSM or cloud-managed keys, a separate issuer integration is needed: the current getLicense() API requires the raw private scalar.
In code the same call is:
#include <stdexcept>
#include <string>
std::string privateKey;
std::string publicKey;
if (!cr::utils::License::generateKeys(privateKey, publicKey))
throw std::runtime_error("Key generation failed");
Step 2. Put the public key into your application
The public key is a plain string. Replace the illustrative value below with the complete key from your own public.key and compile it into your application:
static const std::string PUBLIC_KEY =
"049cf38417a13b8624babd87b5e3dbace0fccedb32ca811c2fce87b30f82e66d15"
"1c660ca8434826acfa4ab54835f1cf8e5fc64c6a03655dfe5b11bdae04e88658";
One key pair covers the whole product, because the modules a license unlocks are named inside the license itself. Use a separate key pair only when you sell genuinely separate products.
Step 3. The customer sends you a token
The customer runs the token tool you ship alongside your application:
./TokenGenerator
Send this token to your software vendor:
208a3310a33ae76291937359ffc79e30
They copy that line and send it to you with their order. To show the same value from inside your own application, call cr::utils::License::getToken(). Run it with the same user account that runs the licensed application, so it sees the same identifiers.
Step 4. Create the license
On your machine, with your private key, run the generator and answer its questions:
./LicenseGenerator
Enter token: 208a3310a33ae76291937359ffc79e30
Enter modules (comma separated, e.g. tracking,detector): tracking,detector
Enter number of days: 365
It writes license.key, one file that unlocks both modules. In code the same call is:
#include <stdexcept>
#include <string>
std::string license;
if (!cr::utils::License::getLicense(token, "tracking,detector", 365,
privateKey, license))
throw std::runtime_error("License generation failed");
Sell a single module by naming just that one, and the whole product by naming them all. Module names are yours to choose; decide them once and keep them stable, because they are written into every license you issue.
Step 5. The customer installs the license
Send the license string to the customer. It is not a signing secret, but it contains a correlatable computer token and can be reused on a cloned installation with the same identifier. Use a delivery channel appropriate for that data; email, a download page or a customer portal can all work.
The customer saves it where your application looks for it. You choose that location; common choices are ~/.config/YourProduct/license.key on Linux and %PROGRAMDATA%\YourProduct\license.key on Windows. The library does not read files itself: your application loads the text and passes it in, which leaves you free to keep the license in a configuration file, a registry value or a database instead.
Step 6. Check the license in your application
#include "License.h"
// Ask about a module where that module is actually used.
void onTrackingRequested()
{
if (!cr::utils::License::checkLicense(license, "tracking", PUBLIC_KEY))
{
std::cout << "No valid license for tracking on this computer."
<< std::endl;
return;
}
startTracking();
}
void onDetectorRequested()
{
if (!cr::utils::License::checkLicense(license, "detector", PUBLIC_KEY))
{
std::cout << "No valid license for detector on this computer."
<< std::endl;
return;
}
startDetector();
}
Check where each module is used and repeat checks as needed in long-running operations. Rechecking observes expiry and module permissions, but is not tamper resistance: patching the shared checking function can still bypass every call. A FALSE result can mean expiry, a different computer, a missing module, malformed input or a cryptographic failure.
To warn a customer before the license runs out, or to show what they have bought:
int daysLeft = 0;
if (cr::utils::License::getLicenseDaysLeft(license, PUBLIC_KEY, daysLeft) &&
daysLeft >= 0 && daysLeft < 14)
std::cout << "Your license expires in " << daysLeft << " days." << std::endl;
std::string modules;
if (cr::utils::License::getLicenseModules(license, PUBLIC_KEY, modules))
std::cout << "Your license covers: " << modules << std::endl;
Common questions
What happens when a license expires while the application is running? Nothing, until you check again. A license is evaluated only when you call checkLicense(...). Call it periodically if you want a long-running process to notice.
How does a customer renew? Issue a new license and replace the old one. Its period starts at issuance, not at the old license’s expiry or the customer’s first use.
What if the customer changes their hardware? The license remains valid while the particular identifier used for issuance is still collected. Replacing a network card normally does not matter when the license uses the OS installation ID. A license bound to the removed card itself will stop matching. An OS reinstall can also change the token. Issue a replacement when the original identifier is no longer available.
Can one license be used on two computers? It is refused when none of the other computer’s collected identifiers matches. Cloned OS images, spoofed identifiers or copied virtual machines can defeat this binding; there is no online duplicate-use detection.
How do I license modules separately? Name them when you issue the license. One license can cover several modules, so a customer still receives a single file, and each module of your application asks about its own name. A license issued for tracking will not unlock detector.
What if a customer later buys another module? Issue a new license naming every module they now own, and they replace the file. There is no need to keep several license files side by side.
Can I cancel a license I have already issued? Not remotely. The library checks offline and has no way to reach you, so an issued license remains valid until its expiry date. Your practical control is the validity period: short terms, renewed regularly, limit the damage.
Does the customer need an internet connection? No. Everything described here works entirely offline.
How strong is this against a determined attacker? With a secure private key and cryptographic implementation, forging new signed license data is not feasible. Local enforcement is different: application patching, identifier cloning and system-clock rollback are outside this library’s protection. UTC encoding avoids timezone errors, not clock tampering. Stronger enforcement needs additional trusted hardware or an online service; short validity periods alone do not prevent rollback.
Build and connect to your project
Requirements: CMake 3.13 or newer, a C++17 compiler and standard library with filesystem support, and OpenSSL 3.x development headers and libraries. GCC 9 or newer, or a current MSVC toolchain, is recommended. OpenSSL is required even when FIPS is not used; a FIPS deployment additionally needs a compatible validated module and its installed configuration.
Typical commands to build License library:
cd License
mkdir build
cd build
cmake ..
make
Table 3 - Build options.
| Option | Default | Description |
|---|---|---|
| CR_LICENSE_SANITIZE | OFF | Build the library and its consumers with ASan/UBSan on non-MSVC toolchains. |
${PARENT}_LICENSE | ON | Build the library. With an empty PARENT, the option is _LICENSE. |
${PARENT}_LICENSE_EXAMPLES | ON standalone, OFF embedded | Build example utilities. |
${PARENT}_LICENSE_TESTS | ON standalone, OFF embedded | Build the test suite. |
| BUILD_TESTING | ON when tests are enabled | Standard CTest switch; OFF also suppresses this project’s tests. |
To include License as source code in a CMake project, follow the steps below. For example, start with this repository structure:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
Create a 3rdparty directory and place the License repository inside it:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
3rdparty
License
Create 3rdparty/CMakeLists.txt with the following content:
cmake_minimum_required(VERSION 3.13)
################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)
################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# Namespace the dependency's build options.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
add_subdirectory(License)
This adds License to the project; examples and tests default to OFF when embedded, while explicit cache choices are preserved. Link the License target to obtain its headers, C++17 requirement and dependencies. Your repository structure will look like:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
License
Add 3rdparty to the root CMakeLists.txt before adding the application directory that links License:
add_subdirectory(3rdparty)
Link the library to your existing application target in src/CMakeLists.txt:
target_link_libraries(yourApplication PRIVATE License)
Replace yourApplication with the actual target name, then include License.h in your source files. Run the tests through CTest or the IDE’s CMake test integration. For sanitizer runs enable CR_LICENSE_SANITIZE; the FIPS helper additionally requires FIPS_PREFIX pointing to a compatible installed FIPS module. Test licenses and tokens require a readable machine identifier. On Windows, use a generator supported by the installed toolchain and select the desired configuration in the IDE.
Example
A complete application that checks a license and refuses to run without one:
#include <fstream>
#include <iostream>
#include <string>
#include "License.h"
// Your own feature switches.
static void enableTracking(bool on) { (void)on; }
static void enableDetector(bool on) { (void)on; }
// Illustrative public key: replace it with your own KeyGenerator output.
static const std::string PUBLIC_KEY =
"049cf38417a13b8624babd87b5e3dbace0fccedb32ca811c2fce87b30f82e66d15"
"1c660ca8434826acfa4ab54835f1cf8e5fc64c6a03655dfe5b11bdae04e88658";
int main()
{
std::cout << "License library " << cr::utils::License::getVersion()
<< std::endl;
std::cout << "Crypto backend: "
<< cr::utils::License::getCryptoBackend()
<< (cr::utils::License::isFipsMode() ? " [FIPS]" : "")
<< std::endl;
// A product should refuse to run when its own cryptography is unwell.
if (!cr::utils::License::selfTest())
{
std::cerr << "Cryptographic self-test failed!" << std::endl;
return -1;
}
// Read the license from wherever your application keeps it.
std::ifstream file("license.key");
if (!file)
{
std::cerr << "No license file found." << std::endl;
std::cout << "Send this token to your vendor: "
<< cr::utils::License::getToken() << std::endl;
return -1;
}
std::string license;
std::getline(file, license);
// A file written on Windows carries a trailing carriage return, which
// is not part of the license string.
while (!license.empty() &&
(license.back() == '\r' || license.back() == '\n'))
license.pop_back();
// "tracking" is the module this program needs in order to start at
// all; optional modules are asked about where they are used.
if (!cr::utils::License::checkLicense(license, "tracking", PUBLIC_KEY))
{
std::cerr << "Invalid license for this computer!" << std::endl;
return -1;
}
int daysLeft = 0;
std::string modules;
if (!cr::utils::License::getLicenseDaysLeft(license, PUBLIC_KEY, daysLeft) ||
!cr::utils::License::getLicenseModules(license, PUBLIC_KEY, modules))
{
std::cerr << "Cannot read license details." << std::endl;
return -1;
}
std::cout << "License valid!" << std::endl;
std::cout << "Modules : " << modules << std::endl;
std::cout << "Days left: " << daysLeft << std::endl;
if (daysLeft < 14)
std::cout << "Warning: this license expires soon." << std::endl;
// Switch features on according to what the license names.
enableTracking(cr::utils::License::checkLicense(license, "tracking",
PUBLIC_KEY));
enableDetector(cr::utils::License::checkLicense(license, "detector",
PUBLIC_KEY));
return 0;
}