2026v0.1.0
licensekit
Python · Flask · SQLite · C++20 · WinHTTP/CNG · Docker
Overview
A software-licensing kit: server, certificate-pinned C++ SDK, one shared conformance suite.
licensekit is a self-contained licensing kit for desktop software: a Flask server, an admin CLI, and a C++20 Windows client SDK, proven against one shared test-vector file. It covers login that never sends the password, machine binding, subscriptions, and signed binary delivery. The kit is the extract of an auth backend that ran live inside a private R&D product, rebuilt with a written wire spec, a threat model, and 84 passing tests.
Where did it come from?
From the auth and subscription backend of a subscription-protected Windows desktop app, a private R&D project. The backend ran live there, gating a distributed client behind real subscriptions, and it did all of that from a single 1,152-line Flask file. The kit keeps the proven protocol and rebuilds the packaging: testable modules instead of one welded file, product endpoints stripped, and the C++ handshake pulled out of a 2,200-line loader, parameterized for any host, port and pin. Two goals on the record: prove the security properties with tests instead of comments, and write the protocol down well enough that other developers can license their own software with it.
How does a login work without sending the password?
The server answers a username with a nonce and the account's salt. The client derives PBKDF2(password, salt) and answers with its HMAC against the nonce, so the password never crosses the wire and neither does the session key. Nonces die after 60 seconds and are consumed inside a lock before any other work, so a replayed login fails even when two requests race. Unknown usernames get a byte-identical challenge with the same amount of work done, because a license server that answers differently leaks its customer list; a test asserts known and unknown users are indistinguishable. Ten failures per username and IP inside a sliding 300-second window locks out both routes. The machine binds on first login in one conditional UPDATE, and SQLite's write lock settles which of two simultaneous machines wins. The cost of an offline guess is priced into the protocol: 600,000 SHA-256 iterations, with the count stored per account so it can rise for new users without breaking deployed clients.
What did the tests catch?
The rebuild paid for itself immediately. The original passwd command claimed a password reset clears the machine binding; the code never touched the field. The port's new tests caught it, the fix landed in the extraction, and a regression test (test_passwd_clears_hwid_binding) now locks the behavior permanently. That was the pattern for the whole port: every security property that used to live as a comment became a named test (test_nonce_replay_is_rejected, test_extend_forward_from_active_expiry), and the time-based logic came out of the route handlers into clock-injectable components. The suite now proves token TTLs, nonce expiry and the throttle window without a single sleep.
Why does the client refuse to trust the network?
Private deployments rarely have certificate authority infrastructure, so the server ships a self-signed cert and the client pins the SHA-256 of its DER encoding as one 64-character string distributed with the binary. The pin replaces chain validation entirely, and the HTTP client refuses to construct in self-signed mode without one. During a handshake the pin is checked before the status code or the first body byte is read. Downloads are signed with a per-user session key derived from the verifier and never transmitted, so the client verifies a payload offline, with no further server contact.
Why is the threat model part of the product?
Because a licensing kit hands other people its security decisions, and those decisions deserve paper. The repo ships two documents as first-class artifacts: PROTOCOL.md, the normative wire spec whose every field name and status code matches the implementation, and THREAT-MODEL.md, which walks each attack from password interception to credential sharing and states the design's answer in plain language. The pricing is part of it: 600,000 iterations works out to roughly 0.1 to 0.2 seconds of modern CPU per offline guess, and the doc does the arithmetic for you. Anyone adopting the kit inherits the reasoning along with the code.
Where does it go next?
The core is stable at v0.1.0 and built to be embedded. The roadmap is concrete: Argon2id for memory hardness once a vetted C++ dependency is acceptable, OPAQUE if the protocol ever starts from scratch, property-based tests for the encodings, and a production WSGI story for larger deployments. The server takes its host, port, TTLs and payload as configuration, and the client takes host, port and pin, so the upgrades land inside the same shape.
Field notes
- 84 pytest tests and 19 Catch2 cases, both suites fed from the same crypto vector file, so a changed hex encoding or HMAC label fails in CI.
- 600,000 PBKDF2-HMAC-SHA256 iterations per login, stored per account; the client accepts any server-sent count up to 10,000,000.
- Single-use nonces expire after 60 seconds; ten failed logins per username and IP inside a sliding 300-second window locks both routes.
- Two bearer tokens per login, 32 random bytes each: 10 minutes and 24 hours. Session state is memory-only, so there is no session store to leak.
- The challenge endpoint answers unknown usernames with a byte-identical shape and the same work; HWID binds on first login in one conditional UPDATE that SQLite's write lock makes race-free.
- Server: 8 modules, 1,564 lines of Python, one runtime dependency (flask). Client: 15 C++ files, ~2,100 lines over WinHTTP and CNG, Windows-only on purpose.
- 401 means a bad proof or token; 403 means the subscription lapsed. Every error is JSON; the API never returns HTML.
- One-command Docker demo: cert generation, demo user seeding and the healthcheck are idempotent at startup.
Source
github.com/notdezzi/licensekit