Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Offline-first software licensing for Nim: compact JWS + Ed25519 verification,
strict offline policy, optional online revocation, opt-in anti-debug gate

clue install softkey (or nimble install softkey)

API reference
Github Actions Github Actions

Features

  • Based on JOSE & Nimcypher
  • Signature-first license checks
  • Strict, predictable license contents
  • Client-side lifetime limits
  • Reserved device binding
  • Advisory online revocation
  • Signed server replies with enforced feature narrowing
  • Complete dev and red-team tooling, no shipped secrets
  • Small footprint on the standard library
  • Optional tamper sensing with a dev bypass

Feature details

Signature-first license checks

Every license is verified as a tamper-proof signed token against an approved key list before any of its contents are trusted.

Strict, predictable license contents

All required fields must be present and correctly typed, duplicate entries are rejected, and oversized tokens are refused.

Client-side lifetime limits

The app caps how long any license may last, with a small allowance for clock differences, so even a valid signature cannot grant a decades-long license.

Reserved device binding

Unbound licenses work today, while hardware-bound licenses are recognized as unsupported until that enforcement is built.

Advisory online revocation

Offline validation always runs first and decides on its own; the server can only add revocation or renewal signals, and the app decides whether an unreachable server fails open or closed.

Signed server replies

Status answers are signed, tied to the exact license shown and a fresh per-request number, expire after five minutes, and may only narrow (never widen) the feature list; unsigned answers are ignored entirely.

Complete dev and red-team tooling

A server-only license signer, a local test server, and an attacker simulator with a scorecard proving forged verdicts never yield a positive signal; no private keys are ever shipped inside the app.

Small footprint

Based on JOSE and Nimcypher, pure Nim cryptography libraries, with network checks built on the standard library alone.

Optional tamper sensing

The app can ask to be told when a debugger is attached and shut itself down; developers can switch this off for everyday work. It slows casual tampering but is not a security boundary.

Examples

Offline validation (deterministic, no network):

import std/times
import softkey

# the single approved signing key, selected by its id
let keys = @[TrustedKey(kid: "license-signing-key-2026-01", pubkey: prodPub)]

# checks the license signature, claims, and lifetime, all offline
let (status, lic) = validateLicenseOffline(token, keys,
  "your-company", "your-product", getTime().toUnix())
if status != valid:
  deny(status)
else:
  echo lic.plan, " ", lic.features

Combined offline + online revocation check (fail-open):

import std/options
import std/times
import softkey

let keys = @[TrustedKey(kid: "license-signing-key-2026-01", pubkey: prodPub)]
let now = getTime().toUnix()

# checks the revocation server address and the fail-open policy
let policy = defaultOnlinePolicy("http://127.0.0.1:18080")

# checks signed server replies against the trusted keys
let checker = httpOnlineChecker(policy, keys, "your-company",
  "your-product")

# checks offline validity first, then layers the online verdict on top
let res = validateLicense(token, keys, "your-company",
  "your-product", now, some(checker), policy)
if res.offline != valid or not isAccepted(res, policy):
  deny(res)

Local mock workflow:

clue install jose nimcypher
clue build tools/mock_server.nim --out:mock_server
./mock_server --port:18080 --seed:tools/revocations.json
clue test

Red-team workflow (adversarial fixture, localhost only):

clue build tools/hacker_server.nim --out:hacker_server
clue build tools/redteam.nim --out:redteam
./redteam --port:18080

Expected oracle: unsigned forgery, random-key signatures, retired kids, wrong nonces/hashes, widened features, stale windows and blackholes all map to onlineUnavailable (never a positive verdict); acceptance then follows the fail-open / requireOnline policy. R2t random-key license tokens stay denied offline.

Anti-debug gate (opt-in; library raises, app terminates):

import std/options
import std/times
import softkey

let keys = @[TrustedKey(kid: "license-signing-key-2026-01", pubkey: prodPub)]
let now = getTime().toUnix()
try:
  # Offline-only with gate:
  let (status, lic) = validateLicenseOffline(token, keys,
    "your-company", "your-product", now,
    defaultAntiDebugPolicy())

  # Or combined with online revocation (checker/policy as above):
  # let res = validateLicense(token, keys, "your-company",
  #   "your-product", now, some(checker), policy,
  #   defaultAntiDebugPolicy())
except DebuggerDetected:
  quit(1)

Dev bypass (debug with softkey enabled; clue build has no --define passthrough, so set it in your app's config.nims):

# config.nims (dev only, never ship)
switch("define", "softkeyNoAntidebug")

Roadmap

  • Strict offline validator (compact JWS + EdDSA, client-capped lifetime)
  • Advisory online composition (CombinedResult + isAccepted, fail-open default)
  • Stdlib mock server with memory + JSON-file revocations
  • Opt-in anti-debug gate (DebuggerDetected + -d:softkeyNoAntidebug dev bypass)
  • Signed + nonce-bound status replies (dedicated license-key signatures, 300s window, enforced feature subsets; redteam R1 now yields unavailable, never valid)
  • Short-lived token guidance (7-30 day exp)
  • Per-action fail-open / fail-closed policy
  • ed25519 device-key binding enforcement
  • Production server auth + TLS pinning, seat / lease tracking
  • Non-goals: self-hash hardening (excluded), JWE-embedded decryption secrets (rejected by design; public verification keys only).

❤ Contributions & Support

🎩 License

MIT license | Nim Community.

About

Offline-first software licensing for Nim apps. Compact JWS + Ed25519 verification. Strict offline policy, optional online revocation, opt-in anti-debug gate

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages