P11ex.Lib (p11ex v0.1.1)
This module contains the core functionality for the P11ex
library and provides
the low-level API for interacting with PKCS#11 modules. In general, you should not
use this module directly. Instead, use the higher-level P11ex.Module
and P11ex.Session
modules instead.
Summary
Types
A mechanism instance represents a cryptographic mechanism with the
associated parameters. A mechanism can either be identified by an
atom (e.g. :aes_cbc
) or a non-negative integer (e.g. 1
). Some
mechanisms require additional parameters that are passed as a map.
Functions
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.Lib.decrypt_init/3
,
P11ex.Lib.decrypt_update/2
, and P11ex.Lib.decrypt_final/1
if you
want to decrypt data in chunks.
Finalize the decryption operation that is in progress for this session
(see P11ex.Lib.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.Lib.decrypt_init/3
).
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
.
Use digest/2
to provide all data at once and get the digest in one go.
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. The session's current operation is set to
:digest
. Use digest_update/2
to provide data to the digest operation.
Call digest_final/1
to finalize the operation and get the digest. Or, call
digest/2
to provide all data at once and get the digest in one go.
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. Consider
using P11ex.Lib.encrypt_init/3
, P11ex.Lib.encrypt_update/2
, and
P11ex.Lib.encrypt_final/1
if you want to encrypt data in chunks.
Finalize the encryption operation that is in progress for this session.
Initialize an encryption operation involving the specified mechanism
and key
. This puts
the session into encryption mode and no other operations can be active at the same time. Use
encrypt_update/2
and encrypt_final/1
to provide data to encrypt and produce the ciphertext
chunks. Consider using encrypt/4
if you want to encrypt data in a single call.
Provide a chunk of plaintext data to the encryption operation that is in
progress for this session (see P11ex.Lib.encrypt_init/3
).
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
. The function returns a tuple with the public
and private key handles.
Generate random data using the token's RNG.
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. The key
's 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
. Use
sign_update/2
to provide data to the signing operation.
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.
Types
@type attributes() :: [attribute()]
@type mechanism_instance() :: {atom()} | {non_neg_integer()} | {atom(), map()} | {non_neg_integer(), map()}
A mechanism instance represents a cryptographic mechanism with the
associated parameters. A mechanism can either be identified by an
atom (e.g. :aes_cbc
) or a non-negative integer (e.g. 1
). Some
mechanisms require additional parameters that are passed as a map.
Example:
{:ckm_aes_cbc, %{iv: iv}}
This is AES in CBC mode with the initialization vector iv
(a binary of 16 bytes).
See P11ex.Session.encrypt_init/3
for examples on how to set the
parameters for the various encryption mechanisms.
Functions
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.Lib.decrypt_init/3
,
P11ex.Lib.decrypt_update/2
, and P11ex.Lib.decrypt_final/1
if you
want to decrypt data in chunks.
@spec decrypt_final(P11ex.Lib.SessionHandle.t()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Finalize the decryption operation that is in progress for this session
(see P11ex.Lib.decrypt_init/3
).
@spec decrypt_init( P11ex.Lib.SessionHandle.t(), mechanism_instance(), 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.Lib.decrypt_update/2
and
P11ex.Lib.decrypt_final/1
.
@spec decrypt_update(P11ex.Lib.SessionHandle.t(), 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.Lib.decrypt_init/3
).
@spec digest(P11ex.Lib.SessionHandle.t(), 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
.
Use digest/2
to provide all data at once and get the digest in one go.
@spec digest_final(P11ex.Lib.SessionHandle.t()) :: {: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(P11ex.Lib.SessionHandle.t(), mechanism_instance()) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a digest operation. The session's current operation is set to
:digest
. Use digest_update/2
to provide data to the digest operation.
Call digest_final/1
to finalize the operation and get the digest. Or, call
digest/2
to provide all data at once and get the digest in one go.
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(P11ex.Lib.SessionHandle.t(), 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.
Encrypt data using the specified mechanism
and key
in a single call. Consider
using P11ex.Lib.encrypt_init/3
, P11ex.Lib.encrypt_update/2
, and
P11ex.Lib.encrypt_final/1
if you want to encrypt data in chunks.
See P11ex.Lib.encrypt_init/3
for examples on how to select an encryption mechanism
and set its parameters.
@spec encrypt_final(P11ex.Lib.SessionHandle.t()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Finalize the encryption operation that is in progress for this session.
@spec encrypt_init( P11ex.Lib.SessionHandle.t(), mechanism_instance(), P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize an encryption operation involving the specified mechanism
and key
. This puts
the session into encryption mode and no other operations can be active at the same time. Use
encrypt_update/2
and encrypt_final/1
to provide data to encrypt and produce the ciphertext
chunks. Consider using encrypt/4
if you want to encrypt data in a single call.
Setting an encryption mechanism
Some mechanisms require additional parameters. These parameters are passed as a
map. The NIF will translate the map into the appropriate PKCS#11 mechanism structure.
If this translation fails (e.g. missing required parameters or wrong type) the operation
will return an error of the form {:error, :invalid_parameter, reason}
.
The following example show how to do this for common mechanisms:
AES ECB
No additional parameters are required for AES ECB mode.
:ok = P11ex.Session.encrypt_init(session, {:ckm_aes_ecb}, key)
AES CBC and AES OFB
These mechanisms require an initialization vector (IV). This IV has to be the same length as the block size of the cipher. For AES the block size is 16 bytes and thus the IV has to be 16 bytes long.
iv = :crypto.strong_rand_bytes(16)
:ok = P11ex.Session.encrypt_init(session1, {:ckm_aes_cbc, %{iv: iv}}, key)
:ok = P11ex.Session.encrypt_init(session2, {:ckm_aes_ofb, %{iv: iv}}, key)
AES CTR
This mechanism requires an initialization vector (IV) and the number of bits in the counter (e.g. 32, 64, 128).
iv = :crypto.strong_rand_bytes(16)
params = %{iv: iv, counter_bits: 32}
:ok = P11ex.Session.encrypt_init(session, {:ckm_aes_ctr, params}, key)
AES GCM
This mechanism has the following additional parameters:
:iv
- the initialization vector (IV). Typically, this is 12 bytes long.:aad
- the optional authentication data (AAD). Not all PKCS#11 tokens support this parameter. Also, the size of the AAD is limited by the token.:tag_bits
- the number of bits in the authentication tag (typically 128)
iv = :crypto.strong_rand_bytes(12)
params = %{iv: iv, tag_bits: 128}
:ok = P11ex.Session.encrypt_init(session, {:ckm_aes_gcm, params}, key)
RSA with PKCS#1 v1.5
This mechanism requires a RSA public key and does not require any additional parameters.
:ok = P11ex.Session.encrypt_init(session, {:ckm_rsa_pkcs}, pub_key)
{:ok, ciphertext} = P11ex.Session.encrypt(session, data)
RSA OAEP
This mechanism requires a RSA public key and the following parameters:
:hash_alg
- the hash algorithm to use.:mgf_hash_alg
- the hash algorithm to use for the mask generation function.:source_data
- the source data to use for the OAEP padding.
The hash_alg
and mgf_hash_alg
parameters identify an hash algorithm in the
same way as the :crypto
module does. That is, possible values are :sha
,
:sha224
, :sha256
, :sha384
, and :sha512
. Support depends on the token.
Example:
:ok = P11ex.Session.encrypt_init(session, {:ckm_rsa_pkcs_oaep, %{hash_alg: :sha, mgf_hash_alg: :sha, source_data: source_data}}, pub_key)
{:ok, ciphertext} = P11ex.Session.encrypt(session, data)
@spec encrypt_update(P11ex.Lib.SessionHandle.t(), 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.Lib.encrypt_init/3
).
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.
Example: Generate a 128-bit AES key
The following example generates a 128-bit AES key with the label "test_key" and a random key ID. The key is a session key.
key_id = :crypto.strong_rand_bytes(16)
{:ok, key} =
Session.generate_key(context.session_pid,
{:ckm_aes_key_gen},
[
{:cka_token, false},
{:cka_label, "test_key"},
{:cka_value_len, key_id},
{:cka_id, key_id},
{:cka_encrypt, true},
{:cka_decrypt, false},
{:cka_derive, false},
{:cka_sign, false}
])
@spec generate_key_pair( P11ex.Lib.SessionHandle.t(), mechanism_instance(), attributes(), 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
. The function returns a tuple with the public
and private key handles.
Example: Generate a RSA key pair
mechanism = {:ckm_rsa_pkcs_key_pair_gen}
pubk_template = [
{:cka_token, false},
{:cka_encrypt, true},
{:cka_verify, true},
{:cka_modulus_bits, 2048},
{:cka_public_exponent, 65537},
{:cka_label, "rsa_test_key"}
]
prvk_template = [
{:cka_token, false},
{:cka_private, true},
{:cka_sensitive, true},
{:cka_decrypt, true},
{:cka_sign, true},
{:cka_label, "rsa_test_key"}
]
{pubk, prvk} =
P11ex.Session.generate_key_pair(session_pid,
{:ckm_rsa_pkcs_key_pair_gen},
pubk_template, prvk_template)
Example: Generate an EC key pair (secp256r1)
See P11ex.ECParam.ec_params_from_named_curve/1
for more functions that
help to create the value of the :cka_ec_params
attribute.
key_id = :crypto.strong_rand_bytes(16)
mechanism = {:ckm_ec_key_pair_gen}
{:ok, params} = ECParam.ec_params_from_named_curve(:secp256r1)
pubk_template = [
{:cka_token, false},
{:cka_key_type, :ckk_ec},
{:cka_verify, true},
{:cka_label, "pubk-secp256r1"},
{:cka_ec_params, params},
{:cka_id, key_id}
]
prvk_template = [
{:cka_token, false},
{:cka_key_type, :ckk_ec},
{:cka_sign, true},
{:cka_label, "prvk-secp256r1"},
{:cka_id, key_id}
]
{:ok, {pubk, prvk}} =
Session.generate_key_pair(context.session_pid,
mechanism, pubk_template, prvk_template)
@spec generate_random(P11ex.Lib.SessionHandle.t(), non_neg_integer()) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
Generate random data using the token's RNG.
@spec session_logout(P11ex.Lib.SessionHandle.t()) :: :ok | {:error, atom()} | {:error, atom(), any()}
@spec sign(P11ex.Lib.SessionHandle.t(), 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(P11ex.Lib.SessionHandle.t()) :: {: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( P11ex.Lib.SessionHandle.t(), mechanism_instance(), P11ex.Lib.ObjectHandle.t() ) :: :ok | {:error, atom()} | {:error, atom(), any()}
Initialize a signing operation or MAC computation. The key
's 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
. Use
sign_update/2
to provide data to the signing operation.
Example: Signing data in chunks
:ok = Session.sign_init(session, {:ckm_rsa_pkcs, %{hash_alg: :sha256}}, priv_key)
:ok = Session.sign_update(session, data1)
:ok = Session.sign_update(session, data2)
{:ok, signature} = Session.sign_final(session)
Example: Signing data in one go
:ok = Session.sign_init(session, {:ckm_rsa_pkcs, %{hash_alg: :sha256}}, priv_key)
{:ok, signature} = Session.sign(session, data)
Signing Mechanisms
RSA PKCS #1 v1.5 Signature and Encryption Mechanism
This mechanism requires a RSA private key and does not require any additional parameters. The digest algorithm to use is specified in the mechanism name. The following mechanisms fall into this category:
:ckm_rsa_pkcs
(uses plain RSA PKCS#1 v1.5 without digest computation):ckm_sha1_rsa_pkcs
:ckm_sha224_rsa_pkcs
:ckm_sha256_rsa_pkcs
:ckm_sha384_rsa_pkcs
:ckm_sha512_rsa_pkcs
Example:
:ok = Session.sign_init(session, {:ckm_sha256_rsa_pkcs}, priv_key)
{:ok, signature} = Session.sign(session, data)
RSA PKCS #1 PSS Signature Mechanism (:ckm_rsa_pkcs_pss
)
This mechanism requires a RSA private key and the following parameters:
:salt_len
- the length of the salt in bytes.:hash_alg
- the hash algorithm to use.:mgf_hash_alg
- the hash algorithm to use for the mask generation function.
The hash_alg
and mgf_hash_alg
parameters identify an hash algorithm in the
same way as the :crypto
module does. That is, possible values are :sha
,
:sha224
, :sha256
, :sha384
, and :sha512
.
Example:
:ok = Session.sign_init(session, {:ckm_rsa_pkcs_pss, %{salt_len: 20, hash_alg: :sha256, mgf_hash_alg: :sha256}}, priv_key)
{:ok, signature} = Session.sign(session, data)
ECDSA Signature Mechanism (:ckm_ecdsa
)
This algorithm requires a pre-computed digest of the data to sign. That is, it does not compute the digest itself.
Example:
data = :crypto.strong_rand_bytes(1024)
digest = :crypto.hash(:sha256, data)
:ok = Session.sign_init(session, {:ckm_ecdsa}, priv_key)
{:ok, signature} = Session.sign(session, digest)
@spec sign_update(P11ex.Lib.SessionHandle.t(), 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(P11ex.Lib.SessionHandle.t(), 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( P11ex.Lib.SessionHandle.t(), 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.
Example: Verifying a RSA PKCS #1 v1.5 signature
:ok = Session.verify_init(session, {:ckm_sha256_rsa_pkcs}, pub_key)
:ok = Session.verify(session, data, signature)