P11ex.Session (p11ex v0.1.1)
This module is a GenServer
that manages a PKCS#11 session. A session is used
to interact with a token, e.g. generate keys, encrypt data, decrypt data, etc. Sessions
are created by the P11ex.Module
module using the open_session/3
function. Depending on
the type of token multiple for the same token can be opened in parallel (e.g. if the token is
a network HSM). One session can only be used in a serialised way, i.e. only one operation can be
performed at a time. Additionally, sessions have a state. This state can be non-persistent keys associated
with the session or the state of an encryption or decryption operation.
Technically, most PKCS#11 functions require to login to the token first using a PIN. That is, the
login state is not connected to a particular session opened on a token. Many tokens raise an :cka_already_logged_in
error if a PIN is provided for a session that is already logged in. The P11ex.Session
module tries to
make handling of the login state more easy by tracking the login state of the session. That is, only the
first call to login/3
will actually login to the token. Subsequent calls to login/3
will check if
the session is already logged in and skip the login if so.
Usage
The following examples show how to log into a token and create a new session.
{:ok, module} = P11ex.Module.start_link("/usr/lib/softhsm/libsofthsm2.so")
{:ok, slot} = P11ex.Module.find_slot_by_tokenlabel("Token_0")
{:ok, session} = P11ex.Session.start_link(module: module, slot_id: slot.slot_id, flags: [:rw_session])
:ok = P11ex.Session.login(session, :user, "1234")
Summary
Functions
Returns a specification to start this module under a supervisor.
Decrypt data using the specified mechanism
and key
in a single call. See
P11ex.Lib.encrypt_init/3
on how to select a decryption mechanism and
set its parameters. Consider using P11ex.Session.decrypt_init/3
,
P11ex.Session.decrypt_update/2
, and P11ex.Session.decrypt_final/1
if you
want to decrypt data in chunks.
Finalize the decryption operation that is in progress for this session
(see P11ex.Session.decrypt_init/3
).
Initialize a decryption operation involving the specified mechanism
and key
. Many
mechanisms require additional parameters. See P11ex.Lib.encrypt_init/3
for more
information on mechanisms and their parameters.
Provide a chunk of ciphertext data to the decryption operation that is in
progress for this session (see P11ex.Session.decrypt_init/3
).
Destroy the object specified by object
.
Get the digest of the data provided to the digest operation. The session must be in the
:digest
state, so this function must be called after digest_init/2
.
Finalize the digest operation. The session must be in the :digest
state,
so this function must be called after digest_init/2
and digest_update/2
.
If the operation fails, the session's current operation is reset. The function
returns the digest.
Initialize a digest operation involving the specified mechanism
. The session's
current operation is set to :digest
. This operation can be finalized by calling
digest_final/1
or digest/1
. Also, a failure of digest_update/2
will end
this state.
Provide data to the digest operation. The session must be in the :digest
state,
so this function must be called after digest_init/2
. Call this function repeatedly
with chunks of data until all data has been provided. If the operation fails, the
session's current operation is reset.
Encrypt data using the specified mechanism
and key
in a single call. See
P11ex.Lib.encrypt/4
on how to select an encryption mechanism and
set its parameters.
Finalize the encryption operation that is in progress for this session.
Initialize an encryption operation involving the specified mechanism
and key
.
Use P11ex.Session.encrypt_update/2
and P11ex.Session.encrypt_final/1
to provide
the data to encrypt and produce the ciphertext chunks. Note that only one encryption
operation can be active at a time for a given session. Consider using P11ex.Session.encrypt/4
if you want to encrypt data in a single call and the data is not too large.
Provide a chunk of plaintext data to the encryption operation that is in
progress for this session (see P11ex.Session.encrypt_init/3
).
Find objects in the session. The attributes
is a list of tuples where the first
element is the attribute type and the second element is the value to match. The
max_hits
is the maximum number of hits to return. The result is a list of
P11ex.Lib.ObjectHandle.t()
objects.
Generate a symmetric key in the session. The key is generated according to the specified mechanism
and the key_template
. The key_template
is a list of attributes that will be used to generate
the key. The function returns a handle to the generated key.
Generate a key pair in the session. The key pair is generated according to the specified mechanism
and the pub_key_template
and priv_key_template
.
Generate random data using the token's RNG.
Get information about the session. The result is a map with the following keys
Initialize the session GenServer
. This requires the :module
(a P11ex.Lib.ModuleHandle.t()
)
and the :slot_id
(an integer) of the slot the session is opened on. Additionally, the :flags
keyword argument can be used to pass additional flags to the open_session/3
function.
Log in to the session. The user_type
must be either :user
or :so
. Provide the user's pin
for authentication. The P11ex.Session
module checks if the session is already logged in and
skips the login if so, preventing :cka_already_logged_in
errors.
Logout from the session.
Read the attributes of the object identified by object handle object
. The type_hint
is an optional and can be used to specify the attributes to read. The default is to read
the common attributes (e.g. :cka_class
, :cka_id
). See P11ex.Lib.ObjectAttributes
for commonly used attribute sets.
Sign or MAC data. The session must be in the :sign
state, so this function
must be called after sign_init/3
. If the operation fails, the session's
current operation is reset. The function returns the signature or MAC.
Finalize the signing operation or MAC computation. The session must
be in the :sign
state, so this function must be called after
sign_init/3
and sign_update/2
. If the operation fails, the session's
current operation is reset. The function returns the signature or MAC.
Initialize a signing operation or MAC computation involving
the specified mechanism
and key
. The key type must be suitable for
the specified mechanism
. If the initialization is successful, the
session's current operation is set to :sign
. This operation can be
finalized by calling sign_final/1
or sign/2
. Also, a failure of
sign_update/2
will end this state.
Provide data to the signing operation or MAC computation. The session must
be in the :sign
state, so this function must be called after sign_init/3
.
Call this function repeatedly with chunks of data until all data has been
provided. If the operation fails, the session's current operation is reset.
Verify a signature or MAC. The session must be in the :verify
state, so this function
must be called after verify_init/3
. If the operation fails, the session's current
operation is reset.
Initialize a verification operation involving the specified mechanism
and key
.
The operation verifies signatures or MACs, depending the mechanism. Some mechanisms
require additional parameters. See P11ex.Lib.sign_init/3
for more information
on mechanisms and their parameters.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor
.
@spec decrypt( server :: GenServer.server(), mechanism :: P11ex.Lib.mechanism_instance(), key :: P11ex.Lib.ObjectHandle.t(), data :: binary() ) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Decrypt data using the specified mechanism
and key
in a single call. See
P11ex.Lib.encrypt_init/3
on how to select a decryption mechanism and
set its parameters. Consider using P11ex.Session.decrypt_init/3
,
P11ex.Session.decrypt_update/2
, and P11ex.Session.decrypt_final/1
if you
want to decrypt data in chunks.
Example: Decrypt data in a single call
{:ok, plaintext} = P11ex.Session.decrypt(session, {:ckm_aes_gcm, %{iv: iv, tag_bits: 128}}, key, ciphertext)
@spec decrypt_final(server :: GenServer.server()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Finalize the decryption operation that is in progress for this session
(see P11ex.Session.decrypt_init/3
).
@spec decrypt_init( server :: GenServer.server(), mechanism :: P11ex.Lib.mechanism_instance(), key :: P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a decryption operation involving the specified mechanism
and key
. Many
mechanisms require additional parameters. See P11ex.Lib.encrypt_init/3
for more
information on mechanisms and their parameters.
The function returns :ok
if the operation is initialized successfully. The session
is now in decryption mode and no other operations can be active at the same time. The
ciphertext can be provided in chunks using P11ex.Session.decrypt_update/2
and
P11ex.Session.decrypt_final/1
.
Consider using P11ex.Session.decrypt/4
if you want to decrypt data in a single call.
Example: Decrypt data in chunks
:ok = P11ex.Session.decrypt_init(session, {:ckm_aes_gcm, %{iv: iv, tag_bits: 128}})
{:ok, plain_chunk1} = P11ex.Session.decrypt_update(session, cipher_chunk1)
{:ok, plain_chunk2} = P11ex.Session.decrypt_update(session, cipher_chunk2)
{:ok, plain_chunk3} = P11ex.Session.decrypt_final(session)
plaintext = plain_chunk1 <> plain_chunk2 <> plain_chunk3
@spec decrypt_update( server :: GenServer.server(), data :: binary() ) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Provide a chunk of ciphertext data to the decryption operation that is in
progress for this session (see P11ex.Session.decrypt_init/3
).
@spec destroy_object(server :: GenServer.server(), P11ex.Lib.ObjectHandle.t()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Destroy the object specified by object
.
@spec digest(server :: GenServer.server(), binary()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Get the digest of the data provided to the digest operation. The session must be in the
:digest
state, so this function must be called after digest_init/2
.
@spec digest_final(server :: GenServer.server()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Finalize the digest operation. The session must be in the :digest
state,
so this function must be called after digest_init/2
and digest_update/2
.
If the operation fails, the session's current operation is reset. The function
returns the digest.
@spec digest_init(GenServer.server(), P11ex.Lib.mechanism_instance()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a digest operation involving the specified mechanism
. The session's
current operation is set to :digest
. This operation can be finalized by calling
digest_final/1
or digest/1
. Also, a failure of digest_update/2
will end
this state.
Example: Digest computation in chunks
:ok = P11ex.Session.digest_init(session, {:ckm_sha256})
:ok = P11ex.Session.digest_update(session, data1)
:ok = P11ex.Session.digest_update(session, data2)
{:ok, digest} = P11ex.Session.digest_final(session)
Example: Digest computation in one go
:ok = P11ex.Session.digest_init(session, {:ckm_sha256})
{:ok, digest} = P11ex.Session.digest(session, data)
@spec digest_update(server :: GenServer.server(), binary()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Provide data to the digest operation. The session must be in the :digest
state,
so this function must be called after digest_init/2
. Call this function repeatedly
with chunks of data until all data has been provided. If the operation fails, the
session's current operation is reset.
@spec encrypt( server :: GenServer.server(), mechanism :: P11ex.Lib.mechanism_instance(), key :: P11ex.Lib.ObjectHandle.t(), data :: binary() ) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Encrypt data using the specified mechanism
and key
in a single call. See
P11ex.Lib.encrypt/4
on how to select an encryption mechanism and
set its parameters.
Example: Encrypt data in a single call
iv = :crypto.strong_rand_bytes(12)
{:ok, ciphertext} = P11ex.Session.encrypt(session, {:ckm_aes_gcm, %{iv: iv, tag_bits: 128}}, key, plaintext)
Finalize the encryption operation that is in progress for this session.
@spec encrypt_init( server :: GenServer.server(), P11ex.Lib.mechanism_instance(), P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize an encryption operation involving the specified mechanism
and key
.
Use P11ex.Session.encrypt_update/2
and P11ex.Session.encrypt_final/1
to provide
the data to encrypt and produce the ciphertext chunks. Note that only one encryption
operation can be active at a time for a given session. Consider using P11ex.Session.encrypt/4
if you want to encrypt data in a single call and the data is not too large.
The function returns :ok
if the operation is initialized successfully. That is, no
other operations (e.g. decryption, signing, etc.) can be active at the same time.
Many mechanisms require additional parameters. See P11ex.Lib.encrypt_init/3
for more
information on mechanisms and their parameters.
Example: Encrypt data in chunks
iv = :crypto.strong_rand_bytes(12)
:ok = P11ex.Session.encrypt_init(session, {:ckm_aes_gcm, %{iv: iv, tag_bits: 128}})
{:ok, cipher_chunk1} = P11ex.Session.encrypt_update(session, plain_chunk1)
{:ok, cipher_chunk2} = P11ex.Session.encrypt_update(session, plain_chunk2)
{:ok, cipher_chunk3} = P11ex.Session.encrypt_final(session)
ciphertext = cipher_chunk1 <> cipher_chunk2 <> cipher_chunk3
@spec encrypt_update( server :: GenServer.server(), data :: binary() ) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Provide a chunk of plaintext data to the encryption operation that is in
progress for this session (see P11ex.Session.encrypt_init/3
).
@spec find_objects( server :: GenServer.server(), attributes :: [{atom(), any()}], max_hits :: non_neg_integer() ) :: {:ok, [P11ex.Lib.ObjectHandle.t()]} | {:error, atom()}
Find objects in the session. The attributes
is a list of tuples where the first
element is the attribute type and the second element is the value to match. The
max_hits
is the maximum number of hits to return. The result is a list of
P11ex.Lib.ObjectHandle.t()
objects.
This example shows how to find all secret keys in the session.
{:ok, objects} = P11ex.Session.find_objects(session, [{:cka_class, :cko_secret_key}], 10)
To find all secret keys with the label "my_key" use the following:
{:ok, objects} = P11ex.Session.find_objects(session, [{:cka_class, :cko_secret_key}, {:cka_label, "my_key"}], 10)
Generate a symmetric key in the session. The key is generated according to the specified mechanism
and the key_template
. The key_template
is a list of attributes that will be used to generate
the key. The function returns a handle to the generated key.
See P11ex.Lib.generate_key/3
for more information on mechanisms and their parameters.
@spec generate_key_pair( server :: GenServer.server(), P11ex.Lib.mechanism_instance(), P11ex.Lib.attributes(), P11ex.Lib.attributes() ) :: {:ok, {P11ex.Lib.ObjectHandle.t(), P11ex.Lib.ObjectHandle.t()}} | {:error, atom()} | {:error, atom(), any()}
Generate a key pair in the session. The key pair is generated according to the specified mechanism
and the pub_key_template
and priv_key_template
.
See P11ex.Lib.generate_key_pair/4
for more information on mechanisms and their parameters.
@spec generate_random(server :: GenServer.server(), len :: non_neg_integer()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
@spec generate_random(server :: GenServer.server(), len :: non_neg_integer()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Generate random data using the token's RNG.
@spec info(server :: GenServer.server()) :: {:ok, map()} | {:error, atom()}
Get information about the session. The result is a map with the following keys:
:slot_id
- the slot ID of the session:state
- the state of the session:flags
- the flags of the session:device_error
- the device error of the session
Initialize the session GenServer
. This requires the :module
(a P11ex.Lib.ModuleHandle.t()
)
and the :slot_id
(an integer) of the slot the session is opened on. Additionally, the :flags
keyword argument can be used to pass additional flags to the open_session/3
function.
@spec login( server :: GenServer.server(), user_type :: {:user, :so}, pin :: String.t() ) :: :ok | {:error, atom()}
Log in to the session. The user_type
must be either :user
or :so
. Provide the user's pin
for authentication. The P11ex.Session
module checks if the session is already logged in and
skips the login if so, preventing :cka_already_logged_in
errors.
@spec logout(server :: GenServer.server()) :: :ok | {:error, atom()}
Logout from the session.
@spec read_object( server :: GenServer.server(), object :: P11ex.Lib.ObjectHandle.t(), type_hint :: [atom()] | nil ) :: {:ok, map(), [atom()]} | {:error, atom()} | {:error, atom(), any()}
Read the attributes of the object identified by object handle object
. The type_hint
is an optional and can be used to specify the attributes to read. The default is to read
the common attributes (e.g. :cka_class
, :cka_id
). See P11ex.Lib.ObjectAttributes
for commonly used attribute sets.
The function returns a map of the successfully read attributes. The attributes that
could not be read (but were requested through the type_hint
) are returned as the
second element of the tuple. Reasons for not retrieving the attributes are that the
attributes are not set or are sensitive.
@spec sign(server :: GenServer.server(), binary()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Sign or MAC data. The session must be in the :sign
state, so this function
must be called after sign_init/3
. If the operation fails, the session's
current operation is reset. The function returns the signature or MAC.
@spec sign_final(server :: GenServer.server()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Finalize the signing operation or MAC computation. The session must
be in the :sign
state, so this function must be called after
sign_init/3
and sign_update/2
. If the operation fails, the session's
current operation is reset. The function returns the signature or MAC.
@spec sign_init( server :: GenServer.server(), P11ex.Lib.mechanism_instance(), P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a signing operation or MAC computation involving
the specified mechanism
and key
. The key type must be suitable for
the specified mechanism
. If the initialization is successful, the
session's current operation is set to :sign
. This operation can be
finalized by calling sign_final/1
or sign/2
. Also, a failure of
sign_update/2
will end this state.
See P11ex.Lib.sign_init/3
for more information on mechanisms and their parameters.
@spec sign_update(server :: GenServer.server(), binary()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Provide data to the signing operation or MAC computation. The session must
be in the :sign
state, so this function must be called after sign_init/3
.
Call this function repeatedly with chunks of data until all data has been
provided. If the operation fails, the session's current operation is reset.
@spec verify(server :: GenServer.server(), binary(), binary()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Verify a signature or MAC. The session must be in the :verify
state, so this function
must be called after verify_init/3
. If the operation fails, the session's current
operation is reset.
The operation return :ok
if the signature (or MAC) is valid. Otherwise, it returns
an error. Typically, the error reason is :ckr_signature_invalid
or :ckr_signature_len_range
.
@spec verify_init( server :: GenServer.server(), P11ex.Lib.mechanism_instance(), P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a verification operation involving the specified mechanism
and key
.
The operation verifies signatures or MACs, depending the mechanism. Some mechanisms
require additional parameters. See P11ex.Lib.sign_init/3
for more information
on mechanisms and their parameters.
If successful, the session is in verification mode and no other operations can be active at the same time.