KeyNub License Dongle — Haskell package
import KeyNub.LicDongle
main :: IO ()
main = withDongle open $ \d -> do -- first dongle, or openSerial "..."
_ <- verifyGenuine d -- throws unless genuine
secret <- withSession d $ -- closed on every exit path
appDecrypt d sealed -- <- build the licence check on this
...
Pure Haskell. The package calls the SDK's flat companion API through
function pointers resolved at run time, so nothing is linked at build time,
there is no C to compile and nothing sits in the path of the check that a
customer could substitute. base, bytestring, directory, filepath and
the platform's loader package (unix, or Win32 on Windows) are the
dependencies. GHC 9.4 or later, on Windows, Linux and macOS.
Setup
cabal install keynub-licdongle
or keynub-licdongle in build-depends. The package does not carry the
native library. Take keynub_licdongle_flat for your platform from the SDK's
natives folder
and either put it where the operating system finds libraries (next to the
executable, or on PATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH), or name it
before the first call:
setLibraryPath "/opt/keynub/libkeynub_licdongle_flat.so"
KEYNUB_LICDONGLE_FLAT_LIBRARY in the environment does the same. In a clone
of the SDK repository the package finds natives/<platform>/ on its own, from
the working directory upwards, so the samples run with nothing set. A process
loads the library once; loadedLibraryPath tells which. On Linux, install the
udev rule described in
NATIVES.md
so the dongle is accessible without root.
Notes
- Results are records (
Info, GenuineResult, Device, RecordInfo); byte
data is ByteString. The package calls the SDK's flat API: integer handles
and caller-provided buffers, no C structures and no hand-written layouts.
- Failures throw
LicDongleError with errorStatus (NoDevice,
NotGenuine, AuthRequired, ...), errorCode, errorOperation and
errorDetail; loading problems throw LibraryError.
isGenuine is the non-throwing form for a gate and fails closed: every
failure gives False.
withDongle and withSession are brackets: the dongle and the session
are closed on every exit path, exceptions included.
eraseAllRecords is deliberately separate from eraseRecord: an
accidentally empty name must not wipe the dongle.
- Records are transferred in one call; the flat API has no progress
reporting.
Read docs/integration-security.md
before writing the check. ok <- isGenuine d; unless ok exitFailure is one
conditional branch, and patching one of those in a release binary is a
beginner exercise. Route something the program needs through appEncrypt and
appDecrypt, so removing the check removes the data.
Tests
cabal test runs without a dongle: it compiles a stand-in for the flat C API
(bindings/flat/licd_flat.c over bindings/julia/test/stub/licd_stub.c) with
the C compiler on the path and exercises every call against it. In a package
taken from Hackage the SDK sources are not present; point KEYNUB_SDK_ROOT at
a clone, or KEYNUB_LICDONGLE_FLAT_LIBRARY at a compiled stand-in.
Links