GitHub - alwaysrun/rlicen: A Rust-based license management system providing comprehensive license generation, verification, and management capabilities. The system supports hardware binding, container binding, and application binding strategies, suitable for physical machines, containers, and development environments.

A Rust-based license management system providing comprehensive license generation, verification, and management capabilities. The system supports hardware binding, container binding, and application binding strategies, suitable for physical machines, containers, and development environments.

Features

  • Flexible Hardware Binding: Support for multiple hardware identifier combinations with configurable strict or loose binding modes
  • Container Environment Support: Specialized identification strategies for containerized applications
  • Development Mode: Minimal binding for development and testing environments
  • C Interface Support: Auto-generated C header files for multi-language integration
  • Command-line Tools: rlgen and rlinspect utilities for license generation and verification
  • Secure Signing: Ed25519 signature algorithm for license security
  • Time Guard: Anti-tampering mechanism to detect system time rollback
  • Cross-platform: Support for Windows and Linux

Host-tag System

The Host-tag system is the core concept of RLicen, defining hardware and system identifiers for license binding through bit flags.

Tag Definitions

Tag Bit Value Description Stability
RL_TAG_CPU 1 << 0 CPU identifier Medium
RL_TAG_MOTHERBOARD 1 << 1 Motherboard serial/UUID High
RL_TAG_DISK 1 << 2 Disk serial number Medium
RL_TAG_MAC 1 << 3 Network MAC address Medium
RL_TAG_MACHINE_ID 1 << 4 System machine ID High
RL_TAG_CONTAINER_ID 1 << 5 Container ID Medium
RL_TAG_OVERLAY_ID 1 << 6 Container overlay ID High
RL_TAG_APP_UUID 1 << 8 Application-generated UUID Low (app-controlled)

Predefined Combinations

Physical Machine - Strict Mode

RL_TAG_CPU | RL_TAG_MOTHERBOARD | RL_TAG_DISK | RL_TAG_MAC | RL_TAG_MACHINE_ID
  • Binds multiple hardware identifiers
  • High security, difficult to forge

Physical Machine - Loose Mode

  • Only binds CPU and MAC addresses

Container - Strict Mode

RL_TAG_OVERLAY_ID | RL_TAG_CONTAINER_ID | RL_TAG_APP_UUID
  • Strong binding to container instance
  • Requires re-issuing license on container rebuild/migration

Container - Loose Mode

RL_TAG_MACHINE_ID | RL_TAG_OVERLAY_ID
  • Allows container migration on same host
  • Binds host machine ID and container overlay

Development Mode

  • Only binds application-generated UUID
  • Maximum flexibility for development and testing
  • Not suitable for production

Installation

Build from Source

# Clone the repository
git clone https://github.com/yourusername/rlicen.git
cd rlicen

# Build the library and tools
cargo build --release

# The following will be generated:
# - target/release/rlicen.dll / librlicen.so (library)
# - target/release/rlgen.exe / rlgen (license generator)
# - target/release/rlinspect.exe / rlinspect (license inspector)

C API

The C header file is automatically generated during build:

  • capi/include/rlicen.h - Auto-generated C header file

Usage

Command-line Tools

1. Generate Key Pairs

# Generate keys in default directory (keys/)
rlgen keys

# Generate keys in custom directory
rlgen keys -o /path/to/keys

Output files:

  • private-{key_id}.pem - Private key for license signing
  • public-{key_id}.pem - Public key for license verification

2. Generate Example Configuration

# Generate default config file
rlgen example

# Generate config file at custom path
rlgen example -o my_config.toml

Example configuration:

product_name = "MyProduct"
product_version = "1.0.0"
host_uid = "[{\"tag\":1,\"name\":\"CPU\",\"uid\":\"SGVsbG8td29ybGQ\"},{\"tag\":8,\"name\":\"MAC\",\"uid\":\"YWJjZGVmZ2hpams\"}]"
valid_from = "2024-01-01"
valid_to = "2024-12-31"
private_key = "keys/private_key.pem"

[extra_data]
feature1 = "enable"
max_users = "100"

3. Generate License

# Generate license from config
rlgen sign -c license_config.toml

# Override expiration date and output file
rlgen sign -c license_config.toml -e "2025-12-31" -o custom_license.xlic

4. Verify License

# Verify license
rlinspect verify -f license.xlic -k keys/public_key.pem

# Inspect license details
rlinspect info -f license.xlic

# Get host identifier
rlinspect host -t PHYSICAL_STRICT

C API Usage

#include "rlicen.h"
#include <stdio.h>

int main() {
    // Set log file
    rl_set_log_file("app.log", RLicenLogLevel_INFO);
    
    // Get version information
    size_t version_size = 0;
    rl_get_version(NULL, &version_size);
    
    char* version = malloc(version_size);
    rl_get_version(version, &version_size);
    printf("RLicen version: %s\n", version);
    free(version);
    
    // Get current machine identifier
    uint32_t tag = RL_TAG_PHYSICAL_STRICT;
    size_t id_size = 0;
    rl_identify_pc(tag, NULL, &id_size, NULL, NULL);
    
    char* identifier = malloc(id_size);
    rl_identify_pc(tag, (uint8_t*)identifier, &id_size, NULL, NULL);
    printf("Host UID: %s\n", identifier);
    free(identifier);
    
    // Verify license
    RLicenVerificationResult result;
    enum RLicenCApiError error = rl_acquire_license(
        "license.xlic",
        "keys/public_key.pem",
        &result
    );
    
    if (error == RLicenCApiError_Ok && result.valid) {
        printf("License valid!\n");
        printf("Product: %s\n", result.product_name);
        printf("Version: %s\n", result.product_version);
    } else {
        printf("License invalid: %s\n", result.reason);
    }
    
    return 0;
}

Rust API Usage

Add to your Cargo.toml:

[dependencies]
rlicen = { path = "./rlicen" }

Example:

use rlicen::{RLicenLicense, RLicenPayload, hardware::identify_pc};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Get host identifier
    let host_uid = identify_pc(RL_TAG_PHYSICAL_STRICT)?;
    println!("Host UID: {}", host_uid);
    
    // Load and verify license
    let license = RLicenLicense::from_file("license.xlic")?;
    let public_key = std::fs::read_to_string("keys/public_key.pem")?;
    
    let result = license.verify(&public_key, RL_TAG_PHYSICAL_STRICT)?;
    
    if result.valid {
        println!("License valid for: {}", result.product_name);
    } else {
        println!("License invalid: {}", result.reason);
    }
    
    Ok(())
}

License Structure

Complete License (RLicenLicense)

{
  "payload": {
    "license_version": 1,
    "license_uuid": "550e8400-e29b-41d4-a716-446655440000",
    "issuer": "MyCompany",
    "issued_at": "2024-01-01",
    "product_name": "MyApp",
    "product_version": "1.0.0",
    "valid_from": "2024-01-01",
    "valid_to": "2024-12-31",
    "host_uid": "[{\"tag\":1,\"name\":\"CPU\",\"uid\":\"SGVsbG8td29ybGQ\"}]",
    "extra_data": {
      "feature1": "enable",
      "max_users": "100"
    }
  },
  "alg": "Ed25519",
  "kid": "gvBJSMu2WWC",
  "signature": "Base64EncodedSignature..."
}

Field Descriptions

Field Type Required Description
payload.license_version u32 Yes License version number (currently 1)
payload.license_uuid String Yes Unique license identifier (UUID v4)
payload.issuer String Yes Issuer identifier
payload.issued_at String Yes Issue date (YYYY-MM-DD)
payload.product_name String Yes Product name
payload.product_version String No Product version
payload.valid_from String Yes Effective date (YYYY-MM-DD)
payload.valid_to String No Expiration date (YYYY-MM-DD), empty means permanent
payload.host_uid String No Host unique identifier (JSON format)
payload.extra_data Object No Custom data (feature flags, limits, etc.)
alg String Yes Signature algorithm (Ed25519)
kid String Yes Key ID (for key rotation)
signature String Yes Signature (Base64 encoded)

Verification Status Codes

Status Code Value Description
Ok 0 Verification successful
Invalid 1 Invalid (general error not related to verification)
InvalidSignature 2 Invalid signature
Expired 3 License expired
NotYetValid 4 License not yet valid
HardwareMismatch 5 Hardware identifier mismatch
InvalidFormat 6 Invalid license format
InvalidKey 7 Invalid key
TimeTampered 8 Time tampered

Documentation

Architecture

RLicen
├── Core Library (lib.rs)
│   ├── License structure definitions (RLicenLicense, RLicenPayload)
│   ├── Verification result structure (RLicenVerificationResult)
│   ├── Status code definitions (RLicenStatusCode)
│   └── C interface exports
├── Hardware Identification Module (hardware/)
│   ├── Hardware identifier collection
│   ├── Host UID generation
│   └── Hardware matching verification
├── Command-line Tools
│   ├── rlgen - License generation tool
│   └── rlinspect - License inspection tool
└── C Interface (capi/)
    ├── C header auto-generation (cbindgen)
    └── C API implementation

Development

Building

# Debug build
cargo build

# Release build
cargo build --release

# Build C library
cargo build --release

Testing

# Run all tests
cargo test

# Run specific test
cargo test test_name

# Run C API tests
cd capi
make run

Code Style

The project follows Rust standard conventions. Use cargo fmt to format code and cargo clippy for linting.

cargo fmt
cargo clippy -- -D warnings

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

Support

For issues, questions, or contributions, please visit the GitHub repository.

Acknowledgments