keynub/licdongle
KeyNub License Dongle: verify that a dongle is genuine, read and write the license records it holds, use its hardware counters and encrypt data so that only a dongle can decrypt it.
import keynub/licdongle
use d <- licdongle.with_dongle(option.None) // first dongle, or Some("serial")
use _ <- result.try(licdongle.verify_genuine(d))
use <- licdongle.with_session(d) // closed on every exit path
licdongle.app_decrypt(d, sealed) // <- build the licence check on this
Calls go to the keynub_licdongle Hex package, whose small NIF loads the
SDK’s native library at run time; nothing is linked.
Types
An attached dongle.
pub type Device {
Device(serial: String, path: String)
}
Constructors
-
Device(serial: String, path: String)
A failed call. CallError: the status, the raw code, the operation (the
flat API function) and the library’s detail text, which may be empty.
LibraryError: the native library could not be loaded, or does not fit.
pub type DongleError {
CallError(
status: Status,
code: Int,
operation: String,
detail: String,
)
LibraryError(message: String)
}
Constructors
-
CallError( status: Status, code: Int, operation: String, detail: String, ) -
LibraryError(message: String)
A record on the dongle.
pub type DongleRecord {
DongleRecord(name: String, size: Int)
}
Constructors
-
DongleRecord(name: String, size: Int)
The result of a successful verify_genuine. provisioned_date is
“YYYY-MM-DD”, or empty when the dongle reports none; informational.
pub type Genuine {
Genuine(serial: String, provisioned_date: String)
}
Constructors
-
Genuine(serial: String, provisioned_date: String)
Plaintext device information. watchdog_reboot: the previous boot ended in
a watchdog reset. write_auth_rotated: the write-auth key has been rotated
away from the factory one.
pub type Info {
Info(
protocol_major: Int,
protocol_minor: Int,
firmware_major: Int,
firmware_minor: Int,
firmware_patch: Int,
secure_element_ready: Bool,
provisioned: Bool,
watchdog_reboot: Bool,
isolated: Bool,
write_auth_rotated: Bool,
data_capacity: Int,
data_free: Int,
)
}
Constructors
-
Info( protocol_major: Int, protocol_minor: Int, firmware_major: Int, firmware_minor: Int, firmware_patch: Int, secure_element_ready: Bool, provisioned: Bool, watchdog_reboot: Bool, isolated: Bool, write_auth_rotated: Bool, data_capacity: Int, data_free: Int, )
The native library’s version.
pub type LibraryVersion {
LibraryVersion(major: Int, minor: Int, patch: Int)
}
Constructors
-
LibraryVersion(major: Int, minor: Int, patch: Int)
Who can decrypt data sealed with app_encrypt: this dongle only
(DeviceScope), or any dongle issued by the same developer
(DeveloperScope).
pub type Scope {
DeviceScope
DeveloperScope
}
Constructors
-
DeviceScope -
DeveloperScope
The SDK’s status codes (licd_status); Unknown carries a code this
package does not know.
pub type Status {
InvalidArg
NoDevice
AccessDenied
Io
Timeout
Protocol
NotGenuine
CertInvalid
SessionExpired
TagMismatch
Range
StorageFull
Busy
NotFound
AuthRequired
FirmwareIncompatible
SdkTooOld
Cancelled
NotImplemented
Internal
Unknown(code: Int)
}
Constructors
-
InvalidArg -
NoDevice -
AccessDenied -
Io -
Timeout -
Protocol -
NotGenuine -
CertInvalid -
SessionExpired -
TagMismatch -
Range -
StorageFull -
Busy -
NotFound -
AuthRequired -
FirmwareIncompatible -
SdkTooOld -
Cancelled -
NotImplemented -
Internal -
Unknown(code: Int)
Values
pub fn app_decrypt(
dongle: Dongle,
packed: BitArray,
) -> Result(BitArray, DongleError)
Opens data sealed with app_encrypt.
pub fn app_encrypt(
dongle: Dongle,
scope: Scope,
plaintext: BitArray,
) -> Result(BitArray, DongleError)
Seals data so that only a dongle can open it: this one (DeviceScope) or
any dongle issued by the same developer (DeveloperScope). Build the licence check
on this pair: put something the program needs through it, so removing the
check removes the data.
pub fn authorize_write(
dongle: Dongle,
key: BitArray,
) -> Result(Nil, DongleError)
Elevates the session to the write role with a write-auth key (P-256 PKCS#8 DER). Belongs in licence-issuing tooling, not in the application your users run.
pub fn close(dongle: Dongle) -> Result(Nil, DongleError)
Closes the dongle. Further calls fail with InvalidArg.
pub fn erase_all_records(
dongle: Dongle,
) -> Result(Nil, DongleError)
Erases every record. Separate from erase_record so that an accidentally
empty name cannot wipe the dongle.
pub fn erase_record(
dongle: Dongle,
name: String,
) -> Result(Nil, DongleError)
Erases one record. Needs the write role.
pub fn increment_counter(
dongle: Dongle,
counter_id: Int,
) -> Result(Int, DongleError)
Increments a counter and returns the new value. Needs the write role.
pub fn is_genuine(dongle: Dongle) -> Bool
The boolean form for a gate: True only when verify_genuine succeeds.
Fails closed: every failure gives False.
pub fn last_error_detail(dongle: Dongle) -> String
Diagnostic detail for the most recent failure on this dongle; may be empty.
pub fn library_version() -> Result(LibraryVersion, DongleError)
The native library’s version.
pub fn loaded_library_path() -> option.Option(String)
The path of the loaded library; None before the first call.
pub fn open(
serial: option.Option(String),
) -> Result(Dongle, DongleError)
Opens the dongle with this serial, or the first one found with None.
close it, or use with_dongle.
pub fn open_path(path: String) -> Result(Dongle, DongleError)
Opens the dongle at this device path (from devices).
pub fn read_counter(
dongle: Dongle,
counter_id: Int,
) -> Result(Int, DongleError)
The value of a hardware monotonic counter.
pub fn read_record(
dongle: Dongle,
name: String,
) -> Result(BitArray, DongleError)
The content of a record.
pub fn records(
dongle: Dongle,
) -> Result(List(DongleRecord), DongleError)
The records on the dongle.
pub fn rotate_write_key(
dongle: Dongle,
key: BitArray,
) -> Result(Nil, DongleError)
Replaces the dongle’s write-auth key with key (P-256 PKCS#8 DER). Call
authorize_write first. From the next session on, only the new key
elevates.
pub fn serial(dongle: Dongle) -> Result(String, DongleError)
The dongle’s serial number (14 hex digits).
pub fn session_close(dongle: Dongle) -> Result(Nil, DongleError)
Closes the session.
pub fn session_open(dongle: Dongle) -> Result(Nil, DongleError)
Opens an authenticated session; records, counters and app crypto need one.
pub fn set_library_path(path: String) -> Result(Nil, DongleError)
Names the native library file to load. Call it before the first dongle call.
pub fn set_trust_root(
dongle: Dongle,
der: BitArray,
) -> Result(Nil, DongleError)
Overrides the CA root that verify_genuine checks against (DER).
pub fn status_text(code: Int) -> String
Human-readable text for a status code; needs no dongle.
pub fn verify_genuine(
dongle: Dongle,
) -> Result(Genuine, DongleError)
Proves the dongle is genuine: certificate chain to the trusted root plus a
live challenge-response. Ok only when it is.
pub fn with_dongle(
serial: option.Option(String),
body: fn(Dongle) -> Result(value, DongleError),
) -> Result(value, DongleError)
Opens a dongle (by serial, or the first one with None), runs body with
it and closes it on every exit path. Returns what body returns, or the
error from opening.
pub fn with_session(
dongle: Dongle,
body: fn() -> Result(value, DongleError),
) -> Result(value, DongleError)
Opens a session, runs body and closes the session on every exit path.
Returns what body returns, or the error from opening the session.
pub fn write_record(
dongle: Dongle,
name: String,
data: BitArray,
) -> Result(Nil, DongleError)
Writes a record, replacing one of the same name. Needs the write role.