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)

An open dongle, from open or with_dongle.

pub type Dongle

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 devices() -> Result(List(Device), DongleError)

The attached dongles.

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 info(dongle: Dongle) -> Result(Info, DongleError)

Plaintext device information.

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_from_code(code: Int) -> Status

The status for a raw code.

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.

✨ Search Document