1:- module(keynub_licdongle,
    2          [ library_environment_variable/1, % -Name
    3            library_basename/1,         % -File
    4            library_candidates/1,       % -Paths
    5            set_library_path/1,         % +Path
    6            library_path/1,             % -Path
    7            loaded_library_path/1,      % -Path
    8            library_version/1,          % -version(Major, Minor, Patch)
    9            status_name/2,              % ?Code, ?Name
   10            status_text/2,              % +Code, -Text
   11            devices/1,                  % -Devices
   12            is_dongle/1,                % @Term
   13            dongle_open/1,              % -Dongle
   14            dongle_open/2,              % +Serial, -Dongle
   15            dongle_open_path/2,         % +Path, -Dongle
   16            dongle_close/1,             % +Dongle
   17            dongle_is_open/1,           % +Dongle
   18            with_dongle/2,              % -Dongle, :Goal
   19            with_dongle/3,              % +Options, -Dongle, :Goal
   20            dongle_serial/2,            % +Dongle, -Serial
   21            dongle_info/2,              % +Dongle, -Info
   22            dongle_verify_genuine/1,    % +Dongle
   23            dongle_verify_genuine/3,    % +Dongle, -Serial, -ProvisionedDate
   24            dongle_genuine/1,           % +Dongle
   25            set_dongle_trust_root/2,    % +Dongle, +Der
   26            dongle_last_error/2,        % +Dongle, -Detail
   27            session_open/1,             % +Dongle
   28            session_close/1,            % +Dongle
   29            with_session/2,             % +Dongle, :Goal
   30            authorize_write/2,          % +Dongle, +Key
   31            rotate_write_key/2,         % +Dongle, +Key
   32            dongle_records/2,           % +Dongle, -Records
   33            read_record/3,              % +Dongle, +Name, -Bytes
   34            write_record/3,             % +Dongle, +Name, +Bytes
   35            erase_record/2,             % +Dongle, +Name
   36            erase_all_records/1,        % +Dongle
   37            read_counter/3,             % +Dongle, +CounterId, -Value
   38            increment_counter/3,        % +Dongle, +CounterId, -Value
   39            app_encrypt/4,              % +Dongle, +Scope, +Plaintext, -Sealed
   40            app_decrypt/3               % +Dongle, +Sealed, -Plaintext
   41          ]).   42:- use_module(library(error)).   43:- use_module(library(lists)).   44:- use_module(library(option)).

KeyNub License Dongle

Verify that a KeyNub dongle is genuine, read and write the license records it holds, use its hardware counters and seal data so that only a dongle can open it.

The pack calls the SDK's flat C API (keynub_licdongle_flat) from the native library, which its foreign module loads at run time on the first call that needs it. Loading this module does not need the native library.

   70:- use_foreign_library(foreign(keynub_licdongle4pl)).   71
   72:- meta_predicate
   73    with_dongle(-, 0),
   74    with_dongle(+, -, 0),
   75    with_session(+, 0).   76
   77:- multifile
   78    prolog:error_message//1.   79
   80:- dynamic
   81    chosen_path/1,
   82    module_folder/1.   83
   84:- prolog_load_context(directory, Dir),
   85   retractall(module_folder(_)),
   86   assertz(module_folder(Dir)).   87
   88                 /*******************************
   89                 *          THE LIBRARY         *
   90                 *******************************/
 library_environment_variable(-Name) is det
The environment variable that names the library file.
   96library_environment_variable('KEYNUB_LICDONGLE_FLAT_LIBRARY').
   97
   98native_folder('win-x64').
   99native_folder('win-x86').
  100native_folder('win-arm64').
  101native_folder('linux-x64').
  102native_folder('linux-arm64').
  103native_folder('osx-x64').
  104native_folder('osx-arm64').
 library_basename(-File) is det
The library's file name on this operating system.
  110library_basename(File) :-
  111    (   current_prolog_flag(windows, true)
  112    ->  File = 'keynub_licdongle_flat.dll'
  113    ;   apple
  114    ->  File = 'libkeynub_licdongle_flat.dylib'
  115    ;   File = 'libkeynub_licdongle_flat.so'
  116    ).
  117
  118apple :-
  119    current_prolog_flag(apple, true),
  120    !.
  121apple :-
  122    current_prolog_flag(arch, Arch),
  123    sub_atom(Arch, _, _, _, darwin),
  124    !.
 set_library_path(+Path) is det
Names the library file to load. Call it before the first dongle call. A process loads the library once; naming a different file after that throws error(keynub_library_error(Message), _).
  132set_library_path(Path) :-
  133    must_be(text, Path),
  134    atom_string(File, Path),
  135    with_mutex(keynub_licdongle, set_library_path_(File)).
  136
  137set_library_path_(File) :-
  138    (   licd_loaded_path(Loaded),
  139        Loaded \== File
  140    ->  format(string(Message),
  141               "the KeyNub library is already loaded from ~w; a process loads it once",
  142               [Loaded]),
  143        throw(error(keynub_library_error(Message), _))
  144    ;   retractall(chosen_path(_)),
  145        assertz(chosen_path(File))
  146    ).
 loaded_library_path(-Path) is semidet
The path the library was loaded from. Fails before the first call that loads it.
  153loaded_library_path(Path) :-
  154    licd_loaded_path(Path).
 library_path(-Path) is det
The path in use, or the first candidate while nothing is loaded.
  160library_path(Path) :-
  161    (   licd_loaded_path(Loaded)
  162    ->  Path = Loaded
  163    ;   library_candidates([Path|_])
  164    ).
 library_candidates(-Paths) is det
The paths tried, in order: the path given to set_library_path/1, else the file named by KEYNUB_LICDONGLE_FLAT_LIBRARY, else every natives/<platform>/<file> found in the program's folder, the working directory or this module's folder or one of their parents, followed by the bare file name for the system loader.
  174library_candidates(Paths) :-
  175    chosen_path(Path),
  176    !,
  177    Paths = [Path].
  178library_candidates(Paths) :-
  179    library_environment_variable(Variable),
  180    getenv(Variable, Path),
  181    Path \== '',
  182    !,
  183    Paths = [Path].
  184library_candidates(Paths) :-
  185    library_basename(Base),
  186    start_folders(Starts),
  187    findall(File,
  188            ( member(Start, Starts),
  189              folder_and_parents(Start, Dirs),
  190              member(Dir, Dirs),
  191              native_folder(Folder),
  192              atomic_list_concat([natives, Folder, Base], /, Relative),
  193              directory_file_path(Dir, Relative, File),
  194              exists_file(File)
  195            ),
  196            Found0),
  197    list_to_set(Found0, Found),
  198    append(Found, [Base], Paths).
  199
  200start_folders(Folders) :-
  201    findall(Folder,
  202            ( start_folder(Folder0),
  203              absolute_file_name(Folder0, Folder1),
  204              no_trailing_slash(Folder1, Folder)
  205            ),
  206            Folders0),
  207    list_to_set(Folders0, Folders).
  208
  209start_folder(Folder) :-
  210    program_file(File),
  211    file_directory_name(File, Folder).
  212start_folder(Folder) :-
  213    working_directory(Folder, Folder).
  214start_folder(Folder) :-
  215    module_folder(Folder).
  216
  217program_file(File) :-
  218    current_prolog_flag(associated_file, File),
  219    !.
  220program_file(File) :-
  221    current_prolog_flag(executable, File).
  222
  223no_trailing_slash(Dir0, Dir) :-
  224    atom_concat(Dir1, '/', Dir0),
  225    Dir1 \== '',
  226    \+ sub_atom(Dir1, _, _, 0, ':'),
  227    !,
  228    Dir = Dir1.
  229no_trailing_slash(Dir, Dir).
  230
  231folder_and_parents(Dir, [Dir|Parents]) :-
  232    file_directory_name(Dir, Parent0),
  233    drive_root(Parent0, Parent),
  234    (   ( Parent == Dir ; Parent == '.' ; Parent == '' )
  235    ->  Parents = []
  236    ;   folder_and_parents(Parent, Parents)
  237    ).
  238
  239drive_root(Dir, Root) :-
  240    atom_length(Dir, 2),
  241    sub_atom(Dir, 1, 1, 0, ':'),
  242    !,
  243    atom_concat(Dir, '/', Root).
  244drive_root(Dir, Dir).
  245
  246ensure_library :-
  247    licd_loaded_path(_),
  248    !.
  249ensure_library :-
  250    with_mutex(keynub_licdongle, load_library).
  251
  252load_library :-
  253    licd_loaded_path(_),
  254    !.
  255load_library :-
  256    library_candidates(Candidates),
  257    load_first(Candidates, []).
  258
  259load_first([], Reasons0) :-
  260    reverse(Reasons0, Reasons),
  261    atomic_list_concat(Reasons, ', ', Tried),
  262    format(string(Message), "cannot load the KeyNub library; tried ~w", [Tried]),
  263    throw(error(keynub_library_error(Message), _)).
  264load_first([Path|Paths], Reasons) :-
  265    os_path(Path, OsPath),
  266    licd_load_library(OsPath, Path, Result),
  267    (   Result == true
  268    ->  true
  269    ;   format(string(Reason), "~w (~w)", [Path, Result]),
  270        load_first(Paths, [Reason|Reasons])
  271    ).
  272
  273os_path(Path, OsPath) :-
  274    (   sub_atom(Path, _, _, _, /)
  275    ->  prolog_to_os_filename(Path, OsPath)
  276    ;   OsPath = Path
  277    ).
 library_version(-Version) is det
The native library's version as version(Major, Minor, Patch).
  283library_version(version(Major, Minor, Patch)) :-
  284    ensure_library,
  285    licd_version(Rc, Major, Minor, Patch),
  286    check(licdf_version, none, Rc).
  287
  288                 /*******************************
  289                 *         STATUS CODES         *
  290                 *******************************/
 status_name(?Code, ?Name) is nondet
Name is the atom for the status code Code (licd_status in the SDK's C header).
  297status_name(0, ok).
  298status_name(-1, invalid_arg).
  299status_name(-2, no_device).
  300status_name(-3, access_denied).
  301status_name(-4, io).
  302status_name(-5, timeout).
  303status_name(-6, protocol).
  304status_name(-7, not_genuine).
  305status_name(-8, cert_invalid).
  306status_name(-9, session_expired).
  307status_name(-10, tag_mismatch).
  308status_name(-11, range).
  309status_name(-12, storage_full).
  310status_name(-13, busy).
  311status_name(-14, not_found).
  312status_name(-15, auth_required).
  313status_name(-16, fw_incompatible).
  314status_name(-17, sdk_too_old).
  315status_name(-18, cancelled).
  316status_name(-19, not_implemented).
  317status_name(-20, internal).
 status_text(+Code, -Text) is det
Human-readable text for a status code, as a string; needs no dongle.
  323status_text(Code, Text) :-
  324    must_be(integer, Code),
  325    ensure_library,
  326    licd_strerror(Code, Rc, Text0),
  327    (   Rc == 0
  328    ->  Text = Text0
  329    ;   status_atom(Code, Name),
  330        atom_string(Name, Text)
  331    ).
  332
  333status_atom(Code, Name) :-
  334    (   status_name(Code, Name0)
  335    ->  Name = Name0
  336    ;   Name = unknown
  337    ).
  338
  339%   keynub_error(+Operation, +Code, +Detail, -Error)
  340%
  341%   The exception term for a failed call.
  342
  343keynub_error(Operation, Code, Detail,
  344             error(keynub_error(Status, Code, Operation, Detail), _)) :-
  345    status_atom(Code, Status).
  346
  347check(_, _, 0) :-
  348    !.
  349check(Operation, Dongle, Rc) :-
  350    detail_of(Dongle, Detail),
  351    keynub_error(Operation, Rc, Detail, Error),
  352    throw(Error).
  353
  354detail_of(Dongle, Detail) :-
  355    (   Dongle \== none,
  356        catch(dongle_last_error(Dongle, Detail0), _, fail)
  357    ->  Detail = Detail0
  358    ;   Detail = ""
  359    ).
  360
  361prolog:error_message(keynub_error(Status, Code, Operation, Detail)) -->
  362    [ '~w: ~w (~w)'-[Operation, Status, Code] ],
  363    (   { Detail == "" }
  364    ->  []
  365    ;   [ ': ~w'-[Detail] ]
  366    ).
  367prolog:error_message(keynub_library_error(Message)) -->
  368    [ '~w'-[Message] ].
  369
  370                 /*******************************
  371                 *     DISCOVERY AND OPENING    *
  372                 *******************************/
 devices(-Devices) is det
The attached dongles, as a list of device(Serial, Path).
  378devices(Devices) :-
  379    ensure_library,
  380    licd_device_count(Rc, Count),
  381    check(licdf_device_count, none, Rc),
  382    Last is Count - 1,
  383    findall(device(Serial, Path),
  384            ( between(0, Last, Index),
  385              licd_device_serial(Index, Rc1, Serial0),
  386              check(licdf_device_serial, none, Rc1),
  387              licd_device_path(Index, Rc2, Path0),
  388              check(licdf_device_path, none, Rc2),
  389              Serial = Serial0,
  390              Path = Path0
  391            ),
  392            Devices).
 is_dongle(@Term) is semidet
True when Term is a dongle from dongle_open/1,2 or dongle_open_path/2, open or closed.
  399is_dongle(Term) :-
  400    licd_is_dongle(Term).
  401
  402must_be_dongle(Dongle) :-
  403    (   licd_is_dongle(Dongle)
  404    ->  true
  405    ;   var(Dongle)
  406    ->  instantiation_error(Dongle)
  407    ;   type_error(keynub_dongle, Dongle)
  408    ).
 dongle_open(-Dongle) is det
 dongle_open(+Serial, -Dongle) is det
Opens the dongle with this serial, or the first one found when Serial is empty or not given. The dongle is closed by dongle_close/1, or when it is garbage collected.
  417dongle_open(Dongle) :-
  418    dongle_open("", Dongle).
  419
  420dongle_open(Serial, Dongle) :-
  421    must_be(text, Serial),
  422    ensure_library,
  423    licd_open(Serial, Rc, Dongle0),
  424    check(licdf_open, none, Rc),
  425    Dongle = Dongle0.
 dongle_open_path(+Path, -Dongle) is det
Opens the dongle at this device path (from devices/1).
  431dongle_open_path(Path, Dongle) :-
  432    must_be(text, Path),
  433    ensure_library,
  434    licd_open_path(Path, Rc, Dongle0),
  435    check(licdf_open_path, none, Rc),
  436    Dongle = Dongle0.
 dongle_is_open(+Dongle) is semidet
True when dongle_close/1 has not been called on Dongle yet.
  442dongle_is_open(Dongle) :-
  443    must_be_dongle(Dongle),
  444    licd_dongle_handle(Dongle, Handle),
  445    Handle > 0.
 dongle_close(+Dongle) is det
Closes the dongle. Closing it again does nothing; other calls on it throw a keynub_error with status invalid_arg.
  452dongle_close(Dongle) :-
  453    must_be_dongle(Dongle),
  454    licd_close(Dongle, Rc),
  455    check(licdf_close, none, Rc).
  456
  457close_quietly(Dongle) :-
  458    catch(dongle_close(Dongle), _, true).
 with_dongle(-Dongle, :Goal) is semidet
 with_dongle(+Options, -Dongle, :Goal) is semidet
Opens a dongle, calls Goal as once/1 and closes the dongle on every exit path, exceptions included. Options select the dongle: serial(Serial) or path(Path); without either, the first one found.
  467with_dongle(Dongle, Goal) :-
  468    with_dongle([], Dongle, Goal).
  469
  470with_dongle(Options, Dongle, Goal) :-
  471    must_be(list, Options),
  472    (   option(serial(Serial), Options),
  473        option(path(Path), Options)
  474    ->  domain_error(serial_or_path, [serial(Serial), path(Path)])
  475    ;   true
  476    ),
  477    setup_call_cleanup(
  478        open_by_options(Options, Dongle),
  479        once(Goal),
  480        close_quietly(Dongle)).
  481
  482open_by_options(Options, Dongle) :-
  483    (   option(path(Path), Options)
  484    ->  dongle_open_path(Path, Dongle)
  485    ;   option(serial(Serial), Options, ""),
  486        dongle_open(Serial, Dongle)
  487    ).
  488
  489                 /*******************************
  490                 *    INFORMATION, AUTHENTICITY *
  491                 *******************************/
 dongle_serial(+Dongle, -Serial) is det
The dongle's serial number (14 hex digits) as a string.
  497dongle_serial(Dongle, Serial) :-
  498    must_be_dongle(Dongle),
  499    ensure_library,
  500    licd_get_serial(Dongle, Rc, Serial0),
  501    check(licdf_get_serial, Dongle, Rc),
  502    Serial = Serial0.
 dongle_info(+Dongle, -Info) is det
Plaintext device information, as a dict with the keys protocol_major, protocol_minor, firmware_major, firmware_minor, firmware_patch and data_capacity, data_free (bytes), and the booleans (true or false) secure_element_ready, provisioned, watchdog_reboot, isolated and write_auth_rotated.
  512dongle_info(Dongle, Info) :-
  513    must_be_dongle(Dongle),
  514    ensure_library,
  515    licd_get_info(Dongle, Rc, Raw),
  516    check(licdf_get_info, Dongle, Rc),
  517    Raw = info(PMajor, PMinor, FMajor, FMinor, FPatch, Flags, Capacity, Free),
  518    flag_value(Flags, 0x01, SecureElementReady),
  519    flag_value(Flags, 0x02, Provisioned),
  520    flag_value(Flags, 0x04, WatchdogReboot),
  521    flag_value(Flags, 0x08, Isolated),
  522    flag_value(Flags, 0x10, WriteAuthRotated),
  523    dict_create(Info, dongle_info,
  524                [ protocol_major-PMajor,
  525                  protocol_minor-PMinor,
  526                  firmware_major-FMajor,
  527                  firmware_minor-FMinor,
  528                  firmware_patch-FPatch,
  529                  secure_element_ready-SecureElementReady,
  530                  provisioned-Provisioned,
  531                  watchdog_reboot-WatchdogReboot,
  532                  isolated-Isolated,
  533                  write_auth_rotated-WriteAuthRotated,
  534                  data_capacity-Capacity,
  535                  data_free-Free
  536                ]).
  537
  538flag_value(Flags, Bit, Value) :-
  539    (   Flags /\ Bit =\= 0
  540    ->  Value = true
  541    ;   Value = false
  542    ).
 dongle_verify_genuine(+Dongle) is det
 dongle_verify_genuine(+Dongle, -Serial, -ProvisionedDate) is det
Proves the dongle is genuine: certificate chain to the trusted root plus a live challenge-response. Succeeds only when it is and throws otherwise. Serial is the verified serial; ProvisionedDate is the day the unit was personalised ("YYYY-MM-DD"), or "" when it reports none.
  552dongle_verify_genuine(Dongle) :-
  553    dongle_verify_genuine(Dongle, _, _).
  554
  555dongle_verify_genuine(Dongle, Serial, ProvisionedDate) :-
  556    must_be_dongle(Dongle),
  557    ensure_library,
  558    licd_verify_genuine(Dongle, Rc, Genuine, Serial0, Date0),
  559    check(licdf_verify_genuine, Dongle, Rc),
  560    (   Genuine =:= 0
  561    ->  keynub_error(licdf_verify_genuine, -7, "", Error),
  562        throw(Error)
  563    ;   Serial = Serial0,
  564        ProvisionedDate = Date0
  565    ).
 dongle_genuine(+Dongle) is semidet
The test for a gate: succeeds only when dongle_verify_genuine/1 succeeds. Fails closed: every error makes it fail.
  572dongle_genuine(Dongle) :-
  573    catch(dongle_verify_genuine(Dongle), Error, genuine_failure(Error)).
  574
  575genuine_failure(Error) :-
  576    (   control_exception(Error)
  577    ->  throw(Error)
  578    ;   fail
  579    ).
  580
  581control_exception(Error) :-
  582    var(Error),
  583    !,
  584    fail.
  585control_exception('$aborted').
  586control_exception(unwind(_)).
  587control_exception(time_limit_exceeded).
  588control_exception(time_limit_exceeded(_)).
 set_dongle_trust_root(+Dongle, +Der) is det
Overrides the CA root (DER bytes) that dongle_verify_genuine/1 checks against. Applications do not need this: the library embeds the KeyNub production root.
  596set_dongle_trust_root(Dongle, Der) :-
  597    must_be_dongle(Dongle),
  598    must_be(text, Der),
  599    ensure_library,
  600    licd_set_trust_root(Dongle, Der, Rc),
  601    check(licdf_set_trust_root, Dongle, Rc).
 dongle_last_error(+Dongle, -Detail) is det
Diagnostic detail for the most recent failure on this dongle, as a string; may be empty.
  608dongle_last_error(Dongle, Detail) :-
  609    must_be_dongle(Dongle),
  610    ensure_library,
  611    licd_last_error(Dongle, Rc, Detail0),
  612    (   Rc == 0
  613    ->  Detail = Detail0
  614    ;   Detail = ""
  615    ).
  616
  617                 /*******************************
  618                 *   SESSIONS AND WRITE ROLE    *
  619                 *******************************/
 session_open(+Dongle) is det
Opens an authenticated session; records, counters and app encryption need one.
  626session_open(Dongle) :-
  627    must_be_dongle(Dongle),
  628    ensure_library,
  629    licd_session_open(Dongle, Rc),
  630    check(licdf_session_open, Dongle, Rc).
 session_close(+Dongle) is det
Closes the session.
  636session_close(Dongle) :-
  637    must_be_dongle(Dongle),
  638    ensure_library,
  639    licd_session_close(Dongle, Rc),
  640    check(licdf_session_close, Dongle, Rc).
 with_session(+Dongle, :Goal) is semidet
Opens a session, calls Goal as once/1 and closes the session on every exit path, exceptions included.
  647with_session(Dongle, Goal) :-
  648    setup_call_cleanup(
  649        session_open(Dongle),
  650        once(Goal),
  651        catch(session_close(Dongle), _, true)).
 authorize_write(+Dongle, +Key) is det
Elevates the session to the write role with a write-auth key (P-256 PKCS#8 DER bytes).
  658authorize_write(Dongle, Key) :-
  659    must_be_dongle(Dongle),
  660    must_be(text, Key),
  661    ensure_library,
  662    licd_write_auth(Dongle, Key, Rc),
  663    check(licdf_write_auth, Dongle, Rc).
 rotate_write_key(+Dongle, +Key) is det
Replaces the dongle's write-auth key with Key (P-256 PKCS#8 DER bytes). Call authorize_write/2 first. From the next session on, only the new key elevates.
  671rotate_write_key(Dongle, Key) :-
  672    must_be_dongle(Dongle),
  673    must_be(text, Key),
  674    ensure_library,
  675    licd_write_auth_rotate(Dongle, Key, Rc),
  676    check(licdf_write_auth_rotate, Dongle, Rc).
  677
  678                 /*******************************
  679                 *            RECORDS           *
  680                 *******************************/
 dongle_records(+Dongle, -Records) is det
The records on the dongle, as a list of record(Name, Size).
  686dongle_records(Dongle, Records) :-
  687    must_be_dongle(Dongle),
  688    ensure_library,
  689    licd_record_count(Dongle, Rc, Count),
  690    check(licdf_record_count, Dongle, Rc),
  691    Last is Count - 1,
  692    findall(record(Name, Size),
  693            ( between(0, Last, Index),
  694              licd_record_name(Dongle, Index, Rc1, Name0, Size0),
  695              check(licdf_record_name, Dongle, Rc1),
  696              Name = Name0,
  697              Size = Size0
  698            ),
  699            Records).
 read_record(+Dongle, +Name, -Bytes) is det
The content of a record, as a list of codes 0..255.
  705read_record(Dongle, Name, Bytes) :-
  706    must_be_dongle(Dongle),
  707    must_be(text, Name),
  708    ensure_library,
  709    licd_record_read(Dongle, Name, Rc, Bytes0),
  710    check(licdf_record_read, Dongle, Rc),
  711    Bytes = Bytes0.
 write_record(+Dongle, +Name, +Bytes) is det
Writes a record, replacing one of the same name. Needs the write role.
  717write_record(Dongle, Name, Bytes) :-
  718    must_be_dongle(Dongle),
  719    must_be(text, Name),
  720    must_be(text, Bytes),
  721    ensure_library,
  722    licd_record_write(Dongle, Name, Bytes, Rc),
  723    check(licdf_record_write, Dongle, Rc).
 erase_record(+Dongle, +Name) is det
Erases the one record Name. Needs the write role.
  729erase_record(Dongle, Name) :-
  730    must_be_dongle(Dongle),
  731    must_be(text, Name),
  732    ensure_library,
  733    licd_record_erase(Dongle, Name, Rc),
  734    check(licdf_record_erase, Dongle, Rc).
 erase_all_records(+Dongle) is det
Erases every record. Needs the write role.
  740erase_all_records(Dongle) :-
  741    must_be_dongle(Dongle),
  742    ensure_library,
  743    licd_record_erase_all(Dongle, Rc),
  744    check(licdf_record_erase_all, Dongle, Rc).
  745
  746                 /*******************************
  747                 *           COUNTERS           *
  748                 *******************************/
 read_counter(+Dongle, +CounterId, -Value) is det
The value of a hardware monotonic counter.
  754read_counter(Dongle, CounterId, Value) :-
  755    must_be_dongle(Dongle),
  756    must_be(integer, CounterId),
  757    ensure_library,
  758    licd_counter_read(Dongle, CounterId, Rc, Value0),
  759    check(licdf_counter_read, Dongle, Rc),
  760    Value = Value0.
 increment_counter(+Dongle, +CounterId, -Value) is det
Increments a counter and returns the new value. Needs the write role.
  766increment_counter(Dongle, CounterId, Value) :-
  767    must_be_dongle(Dongle),
  768    must_be(integer, CounterId),
  769    ensure_library,
  770    licd_counter_increment(Dongle, CounterId, Rc, Value0),
  771    check(licdf_counter_increment, Dongle, Rc),
  772    Value = Value0.
  773
  774                 /*******************************
  775                 *      APP-DATA ENCRYPTION     *
  776                 *******************************/
 app_encrypt(+Dongle, +Scope, +Plaintext, -Sealed) is det
Seals Plaintext so that only a dongle can open it: this one (Scope device) or any dongle issued by the same developer (Scope developer). Sealed is a list of codes 0..255.
  784app_encrypt(Dongle, Scope, Plaintext, Sealed) :-
  785    must_be_dongle(Dongle),
  786    must_be(oneof([device, developer]), Scope),
  787    must_be(text, Plaintext),
  788    scope_value(Scope, Value),
  789    ensure_library,
  790    licd_app_encrypt(Dongle, Value, Plaintext, Rc, Sealed0),
  791    check(licdf_app_encrypt, Dongle, Rc),
  792    Sealed = Sealed0.
  793
  794scope_value(device, 0).
  795scope_value(developer, 1).
 app_decrypt(+Dongle, +Sealed, -Plaintext) is det
Opens data sealed with app_encrypt/4. Plaintext is a list of codes 0..255.
  802app_decrypt(Dongle, Sealed, Plaintext) :-
  803    must_be_dongle(Dongle),
  804    must_be(text, Sealed),
  805    ensure_library,
  806    licd_app_decrypt(Dongle, Sealed, Rc, Plaintext0),
  807    check(licdf_app_decrypt, Dongle, Rc),
  808    Plaintext = Plaintext0