Verifies that a 'KeyNub' USB license dongle is genuine, reads and writes the license records it holds, reads and increments its monotonic counters, and encrypts data so that only a dongle can decrypt it. The work is done by the vendor's native library, which the package loads at run time; the package installs without it and reports where it looked. Runs on Windows, Linux and macOS.
library(KeyNubLicDongle)
ctx <- licd_context()
dongle <- licd_open(ctx) # first dongle, or licd_open(ctx, serial)
licd_verify_genuine(dongle) # signals an error unless genuine
data <- licd_with_session(dongle,
licd_app_decrypt(dongle, sealed)) # <- build the licence check on this
licd_close(dongle)
licd_close(ctx)
No packages. The R code reaches the SDK's C library through a small C layer that R compiles when the package is installed, so nothing sits in the path of the check that a customer could substitute.
R is the closest neighbour to the MATLAB and Julia bindings, and sells to the
same kind of buyer. If you sell an R package, the thing worth protecting is
usually not the code but the data: fitted parameters, a validated correlation
set, a proprietary model's coefficients. That is what
licd_app_encrypt()/licd_app_decrypt() is for.
install.packages("KeyNubLicDongle")
CRAN ships the package compiled for Windows and macOS; on Linux, R compiles the C layer with the system compiler, as for any source package. Straight from the repository instead:
install.packages("remotes")
remotes::install_github("AB-KeyNub/KeyNub-SDK", subdir = "bindings/r")
The package loads the native library at run time and does not carry it. Take
keynub_licdongle for your platform from the SDK's
natives folder
and either put it where the operating system finds libraries (PATH,
LD_LIBRARY_PATH, DYLD_LIBRARY_PATH) or name it before the first call:
licd_library("/opt/keynub/libkeynub_licdongle.so")
KEYNUB_LICDONGLE_LIBRARY in the environment does the same. In a clone of the
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; licd_library() tells which. On Linux, install the udev rule
described in
NATIVES.md
so the dongle is accessible without root.
licd_info() a list, licd_enumerate() and
licd_record_list() data frames, records and sealed data raw vectors
(rawToChar() for text). Counters are unsigned 32-bit and come back as
doubles.licd_error carrying status,
operation and detail. The ones a program branches on carry a more
specific class in front: licd_no_device, licd_not_genuine,
licd_certificate_invalid, licd_session_expired, licd_not_found,
licd_auth_required, licd_cancelled. So
tryCatch(licd_open(ctx), licd_no_device = function(e) NULL).licd_is_genuine(dongle) is the non-signalling form for a gate and fails
closed.licd_with_session(dongle, expr) closes the session afterwards, whatever
happens inside expr.licd_record_read() and licd_record_write() take
progress = function(done, total); returning FALSE cancels. An error inside
the function cancels the transfer rather than unwinding through the C frames,
which would strand the device.licd_record_erase_all() is deliberately separate from licd_record_erase():
to the C library a missing name means "erase every record", and an
accidentally empty variable must not do that.licd_close() releases a dongle or a context, and the garbage collector
releases what is forgotten: a context reclaimed with dongles still open closes
them first.system.file("include", "licdongle.h", package = "KeyNubLicDongle"), for code
that talks to the library directly.Read
docs/integration-security.mdbefore writing the check.if (!licd_is_genuine(dongle)) stop()is one line to delete, and R ships as source. What cannot be deleted is data the program needs and only the dongle can decrypt.
tests/stub.R runs without a dongle: it compiles a stand-in for the C ABI from
tests/licd_stub.c with R's own toolchain (R CMD SHLIB) and exercises every
call against it, which is what R CMD check runs.