A GenServer that represents a single PKCS#11 token in a single slot. A Token owns the
login state and the session bookkeeping for that slot only. This is deliberately narrower
than P11ex.Module, which represents the whole loaded library and may have several tokens
(several Token processes) open behind it at once.
PKCS#11 login state is scoped per token: a successful C_Login on any session to a token
authenticates every session to that token, not to other tokens reachable through the same
library. Token tracks that state itself, so several tokens sharing one P11ex.Module each
authenticate independently and correctly.
Usage
{:ok, module} = P11ex.Module.start_link("/usr/lib/softhsm/libsofthsm2.so")
{:ok, slot} = P11ex.Module.find_slot_by_tokenlabel(module, "Token_0")
{:ok, token} = P11ex.Token.start_link(module: module, slot_id: slot.slot_id)
:ok = P11ex.Token.login(token, :user, "1234")
{:ok, session} = P11ex.Session.start_link(token: token, flags: [:serial_session])Once login/3 succeeds, sessions opened afterwards through open_session/2 (or through
P11ex.Session.start_link/1 with this token) do not need to log in themselves — PKCS#11
login state is shared across every session to the token once any one of them is
authenticated. login/3 keeps an internal control session open for the token's lifetime to
anchor that state, independent of how many other sessions are opened and closed.
Summary
Functions
Returns a specification to start this module under a supervisor.
Close a session previously opened with open_session/2.
Query information about the token in this token's slot. See P11ex.Lib.token_info/2
for details on the returned map.
GenServer callback. Initializes the token's state from the args passed to start_link/1;
see start_link/1 for the required and optional keys.
Log in to the token. user_type must be :user or :so. If the token is already logged in
as user_type, this is a no-op. If it is logged in as a different user type, this returns
{:error, :ckr_user_already_logged_in} rather than attempting a conflicting login.
Return the cached login state for this token: :user, :so, or nil. This is a cheap
in-memory read that does not perform any PKCS#11 I/O; it reflects the last login/logout
this Token process performed (or the last refresh_login_type/1 call), and can go stale
if the token's login state changes through another session or process. Use
refresh_login_type/1 to query the token directly.
Log out of the token and close its internal control session. A no-op if not logged in.
Open a new PKCS#11 session on this token's slot. flags is a list of atoms accepted by
P11ex.Lib.open_session/3 (e.g. :rw_session, :serial_session).
Query the token directly for its current login state via C_GetSessionInfo on the internal
control session, and update the cached value returned by login_type/1 to match. Unlike
login_type/1, this performs PKCS#11 I/O, so it also reflects logins/logouts made outside
this Token process (e.g. another session on the same token). Returns {:ok, :user},
{:ok, :so}, or {:ok, nil} if no user is logged in, or {:error, reason} if the query
fails (e.g. an invalid session, device, or network error). If no control session exists yet
(the token has never been logged in through this process), returns {:ok, nil} without
performing any PKCS#11 call.
Start the P11ex.Token GenServer.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec close_session(GenServer.server(), P11ex.Lib.SessionHandle.t()) :: :ok | {:error, term()}
Close a session previously opened with open_session/2.
@spec info(GenServer.server()) :: {:ok, P11ex.Lib.Slot.token_info()} | {:error, term()}
Query information about the token in this token's slot. See P11ex.Lib.token_info/2
for details on the returned map.
GenServer callback. Initializes the token's state from the args passed to start_link/1;
see start_link/1 for the required and optional keys.
@spec login(GenServer.server(), atom(), binary()) :: :ok | {:error, term()}
Log in to the token. user_type must be :user or :so. If the token is already logged in
as user_type, this is a no-op. If it is logged in as a different user type, this returns
{:error, :ckr_user_already_logged_in} rather than attempting a conflicting login.
On first successful login, an internal control session is opened and kept for the token's
lifetime (or until logout/1) to anchor the login state, independent of any other sessions
opened via open_session/2.
@spec login_type(GenServer.server()) :: atom() | nil
Return the cached login state for this token: :user, :so, or nil. This is a cheap
in-memory read that does not perform any PKCS#11 I/O; it reflects the last login/logout
this Token process performed (or the last refresh_login_type/1 call), and can go stale
if the token's login state changes through another session or process. Use
refresh_login_type/1 to query the token directly.
@spec logout(GenServer.server()) :: :ok | {:error, term()}
Log out of the token and close its internal control session. A no-op if not logged in.
@spec open_session(GenServer.server(), [atom()]) :: {:ok, P11ex.Lib.SessionHandle.t()} | {:error, term()}
Open a new PKCS#11 session on this token's slot. flags is a list of atoms accepted by
P11ex.Lib.open_session/3 (e.g. :rw_session, :serial_session).
@spec refresh_login_type(GenServer.server()) :: {:ok, :user | :so | nil} | {:error, term()}
Query the token directly for its current login state via C_GetSessionInfo on the internal
control session, and update the cached value returned by login_type/1 to match. Unlike
login_type/1, this performs PKCS#11 I/O, so it also reflects logins/logouts made outside
this Token process (e.g. another session on the same token). Returns {:ok, :user},
{:ok, :so}, or {:ok, nil} if no user is logged in, or {:error, reason} if the query
fails (e.g. an invalid session, device, or network error). If no control session exists yet
(the token has never been logged in through this process), returns {:ok, nil} without
performing any PKCS#11 call.
@spec start_link(Keyword.t()) :: GenServer.on_start()
Start the P11ex.Token GenServer.
:module(required) - aGenServer.server()for a runningP11ex.Module:slot_id(required) - the integer slot id, e.g. fromP11ex.Module.find_slot_by_tokenlabel/2:name(optional) - registers the GenServer under that name