Skip to main content

PKI Architecture for Offline License Verification

Overview​

Yabanin uses an offline PKI hierarchy to manage on-premise license tokens. There is no phone-home requirement: every verification happens locally using Ed25519 cryptographic signatures.

┌───────────────────────────────────────────────────────┐
│ VENDOR (Air-Gapped) │
│ │
│ ┌─────────────────────┐ │
│ │ ROOT KEY │ Ed25519 │
│ │ (never online) │ Kept offline / HSM │
│ └─────────┬───────────┘ │
│ │ sign │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ INTERMEDIATE CERT │ Ed25519 │
│ │ + signing key │ Signed by root │
│ └─────────┬───────────┘ │
│ │ sign │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ LICENSE TOKEN │ Ed25519 │
│ │ (per-customer) │ Signed by intermediate │
│ └─────────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ CRL (optional) │ Revoked intermediate serials │
│ └─────────────────────┘ │
└───────────────────────────────────────────────────────┘
│ deliver (USB / secure email)
▼
┌───────────────────────────────────────────────────────┐
│ CUSTOMER ON-PREM ENVIRONMENT │
│ │
│ Embedded root public key (built into binary) │
│ Intermediate cert (deployed with release) │
│ License token (per-customer JSON file) │
│ CRL (optional, deployed separately) │
│ │
│ Startup: VerifyLicenseChain() │
│ 1. Parse license token │
│ 2. Check expiry │
│ 3. Load & verify intermediate cert against root │
│ 4. Verify license sig against intermediate pubkey │
│ 5. (Optional) Check CRL for revoked intermediates │
└───────────────────────────────────────────────────────┘

Trust Model​

Root Key (generate-root)​

  • Ed25519 keypair, never leaves the vendor's air-gapped environment.
  • The public key is embedded into every production binary at build time via keys_gen.go (run with YABANIN_LICENSE_ROOT_PUBKEY env var).
  • If compromised, the entire PKI must be re-issued.

Intermediate Certificate (issue-intermediate)​

  • A short-lived Ed25519 keypair (default 90 days) signed by the root.
  • Used exclusively for issuing customer license tokens.
  • When an intermediate expires or is suspected compromised:
    1. Generate a new intermediate from root.
    2. Publish a CRL listing the old serial as revoked.
    3. Deploy new cert + CRL to customers.

License Token (issue)​

  • Per-customer JSON token containing customer_id, expiry, serial.
  • Signed by the current intermediate key.
  • Delivered to the customer (USB, secure email, portal download).
  • Customer places it on their server and sets env vars:
    • YABANIN_LICENSE_FILE — path to the JSON token
    • YABANIN_LICENSE_ROOT_PUBKEY — root public key hex (64 chars)
    • YABANIN_LICENSE_CRL_FILE — optional CRL path

CRL (crl.go)​

  • Optional JSON file listing revoked intermediate certificate serials.
  • Used to invalidate compromised/expired intermediates without rebuilding binaries.

Key Generation & Deployment Flow​

Vendor air-gap:
yabanin license gen-root --key-file=root
yabanin license issue-intermediate --root-key=root.priv --validity=90 \
--key-file=inter-2027q1 --serial=...
yabanin license issue --customer="acme-corp" --expiry=2027-12-31 \
--inter-key=inter-2027q1.priv --inter-serial=<serial> \
--output=acme-corp-license.json

Build production binary:
YABANIN_LICENSE_ROOT_PUBKEY=<root.pub hex> go generate ./pkg/license/...
make all

Deploy to customer:
Place acme-corp-license.json on server
Set env vars in systemd/docker environment
Start admin/gateway — license verified at startup

Security Considerations​

RiskMitigation
Root key compromiseKeep root offline; rotate intermediates regularly
Intermediate key theftShort validity (90d); CRL for revocation
License token theftBind to customer org_id; expiry enforcement
Binary tamperingRoot pubkey embedded at build time
Replay of old licensesExpiry + grace period (7 days)