P11ex.Token (p11ex v0.5.0)

Copy Markdown View Source

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.

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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

close_session(server \\ __MODULE__, session_handle)

@spec close_session(GenServer.server(), P11ex.Lib.SessionHandle.t()) ::
  :ok | {:error, term()}

Close a session previously opened with open_session/2.

info(server \\ __MODULE__)

@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.

init(args)

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.

login(server \\ __MODULE__, user_type, pin)

@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.

login_type(server \\ __MODULE__)

@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.

logout(server \\ __MODULE__)

@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.

open_session(server \\ __MODULE__, flags)

@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).

refresh_login_type(server \\ __MODULE__)

@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.

start_link(args)

@spec start_link(Keyword.t()) :: GenServer.on_start()

Start the P11ex.Token GenServer.