Ed25519 Signatures
License payloads signed with Ed25519 via OpenSSL EVP. Fast, compact 64-byte signatures with strong security guarantees.
A self-contained offline license verification system using Ed25519 signatures via OpenSSL, written in portable C11. It includes a full suite of hardening features beyond a basic sign/verify proof of concept — trust stores, key rotation, machine binding, anti-rollback, audit logging, and binary integrity checking.
Vendor (offline) Client (offline)
┌────────────────────┐ ┌─────────────────────────┐
│ private key(s) │ │ embedded trust store │
│ + signer tool │──▶ license ──▶ │ (multiple public keys) │
│ + root key │ │ + verifier │
│ + trust update │──▶ .trust ──▶ │ + machine binding │
│ tool │ │ + anti-rollback state │
└────────────────────┘ │ + audit logging │
│ + binary integrity │
└─────────────────────────┘
Every layer is designed to make offline license verification robust against tampering, replay, and unauthorized use.
License payloads signed with Ed25519 via OpenSSL EVP. Fast, compact 64-byte signatures with strong security guarantees.
Multiple versioned public keys with retirement support. V2 licenses specify issuer_key_id for trust store lookup.
Distribute new public keys without recompiling the client. Updates are signed by a root key for authenticity.
SHA-256 of platform-specific machine ID. Supports Linux /etc/machine-id, macOS IOPlatformUUID, and Windows MachineGuid.
HMAC-protected per-license timestamps detect clock rollback attempts. 60-second tolerance for minor drift.
Append-only log with HMAC chain. Each entry covers the previous entry's HMAC forming a tamper-evident chain.
Post-link SHA-256 self-hash detects binary patching. The verifier re-reads itself at runtime and compares hashes.
Configurable grace window past license expiry. Returns LIC_GRACE status so your application can warn users.
Create Ed25519 key pairs and embed public keys into the client binary
Vendor signs a license payload with the private key using the signer tool
Send the .lic file to the customer — no network required
Client verifies signature, checks constraints, and enforces all hardening
-----BEGIN LICENSE-----
format_version=1
license_id=LIC-2026-0001
customer_name=Example Customer
product=ExampleProduct
edition=pro
features=core,export
not_before=2026-01-01T00:00:00Z
not_after=2027-01-01T00:00:00Z
machine_id=
-----SIGNATURE-----
<base64 Ed25519 signature>
-----END LICENSE-----
-----BEGIN LICENSE-----
format_version=2
issuer_key_id=ed25519-2026-01
license_id=LIC-2026-0001
customer_name=Example Customer
product=ExampleProduct
edition=pro
features=core,export
not_before=2026-01-01T00:00:00Z
not_after=2027-01-01T00:00:00Z
machine_id=
-----SIGNATURE-----
<base64 Ed25519 signature>
-----END LICENSE-----
UTF-8 payload with one key=value per line, LF endings, deterministic order. Ed25519 signature computed over exact payload bytes between BEGIN and SIGNATURE markers.
Get up and running in seconds. Only requires OpenSSL 1.1.1+, a C compiler, and make.
# Full build + all 9 test suites
make clean test
# Or step by step:
# 1. Generate keys + embedded headers
sh tools/generate_test_keys.sh . include
# 2. Build all binaries
make
# 3. Issue a sample license
sh tools/make_sample_license.sh ./license_sign private_key.pem sample.lic
# 4. Verify
./license_verify sample.lic ExampleProduct
# 5. Verify with all hardening enabled
./license_verify sample.lic ExampleProduct \
--state-dir ./state \
--audit-log ./audit.log \
--check-integrity
make test runs 9 test suites covering every hardening feature:
| Test | What it proves |
|---|---|
| test-basic | V1 license sign → verify round-trip |
| test-tamper | Payload corruption and product mismatch detected |
| test-v2 | V2 format with issuer_key_id + trust store lookup |
| test-machine | Machine fingerprint + wrong-ID rejection |
| test-rollback | Anti-rollback state creation and re-verification |
| test-audit | Audit log creation and append |
| test-trust-update | Trust update: reject unknown key → accept after update |
| test-grace | Expired license rejected → accepted with grace |
| test-integrity | Unpatched warns → patched verifies correctly |
license_verify <license.lic> [product] [options...]
Options:
--grace <seconds> Grace period past expiry
--machine-required Require machine_id to match
--state-dir <path> Anti-rollback state directory
--audit-log <path> Audit log file path
--trust-update <path> Apply signed trust anchor update
--check-integrity Binary self-integrity check
--dump-machine-id Print machine fingerprint and exit
--dump-truststore Print trust store and exit
license_sign [--key-id <id>] <privkey.pem> <payload.txt> <output.lic>
trust_update_sign <root_privkey.pem> <update_id> <output.trust> \
<key_id> <pubkey.pem> [retired <retire_after>] ...
integrity_patch <binary>
Trust model: The vendor private keys are the root of trust. The root key is the ultimate trust anchor for key rotation.
What signatures protect: Integrity and authenticity of the license payload.
Explicit non-goals: This is not anti-debug, not obfuscation, not a DRM system. It protects license integrity for honest deployments, not against a determined attacker with full binary access.
Portable C11. No dependencies beyond OpenSSL. One make to build, one make test to verify.