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.
This functions interprets the byte array wrapped_key_bytes as an encrypted key,
decrypts it using the unwrapping key unwrapping_key_handle, and creates a private key
or secret key object in the session or token. See wrap_key/4 for more information on
wrapping keys. Typically, these functions are use to import externalized keys or to migrate
keys between tokens.
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.
This functions wraps (encrypts) a key using a wrapping key. The result is a byte
array that contains the encrypted key. The function unwrap_key/5 can be used to
decrypt the key and load it into the session or token. Typically, these functions
are use to externalize keys (e.g. for storing them in a database), to import
pre-existing keys, or to migrate keys between tokens.
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:
source_data = :crypto.strong_rand_bytes(16)
: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)Example: Generate an EdDSA key pair (edwards25519)
# OID for edwards25519, use 1.3.101.113 for edwards448
{:ok, ec_params} = :EC.encode(:ECParametersForEDDSA, {1, 3, 101, 112})
mechanism = {:ckm_ec_edwards_key_pair_gen}
pubk_template = [
{:cka_token, false},
{:cka_label, "pubk-edwards25519"},
{:cka_ec_params, ec_params},
{:cka_id, key_id}
]
prvk_template = [
{:cka_token, false},
{:cka_label, "prvk-edwards25519"},
{: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 load_module(String.t()) :: {:ok, P11ex.Lib.ModuleHandle.t()} | {:error, String.t()}
@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)The signature is returned as a binary containing the r and s values of the signature
without any ASN.1 encoding. See P11ex.ECSignature.recode_as_asn1/1 for a function to convert
the signature to the ASN.1 encoding.
EdDSA Signature Mechanism (:ckm_eddsa)
The alorithm is PureEd25519 or PureEd448. That is, you need to provide the full data to sign as an input to the sign operation. The signature is 64 bytes for PureEd25519 and 114 bytes for PureEd448.
Example:
data = :crypto.strong_rand_bytes(1024)
:ok = Session.sign_init(session, {:ckm_eddsa}, priv_key)
{:ok, signature} = Session.sign(session, data)
# The signature is the concatenation of the r and s values
<<r::binary-size(32), s::binary-size(32)>> = signature
@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 unwrap_key( P11ex.Lib.SessionHandle.t(), mechanism_instance(), P11ex.Lib.ObjectHandle.t(), binary(), attributes() ) :: {:ok, P11ex.Lib.ObjectHandle.t()} | {:error, atom()} | {:error, atom(), any()}
This functions interprets the byte array wrapped_key_bytes as an encrypted key,
decrypts it using the unwrapping key unwrapping_key_handle, and creates a private key
or secret key object in the session or token. See wrap_key/4 for more information on
wrapping keys. Typically, these functions are use to import externalized keys or to migrate
keys between tokens.
The set of supported combinations of unwrapping key type, wrapped key type, and mechanism
is limited and mostly specific to the token. See wrap_key/4 for more information on this
topic.
Unwrapping keys are secret keys or private keys that have the :cka_unwrap attribute
set to true.
@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)
@spec wrap_key( P11ex.Lib.SessionHandle.t(), mechanism_instance(), P11ex.Lib.ObjectHandle.t(), P11ex.Lib.ObjectHandle.t() ) :: {:ok, binary()} | {:error, atom()} | {:error, atom(), any()}
This functions wraps (encrypts) a key using a wrapping key. The result is a byte
array that contains the encrypted key. The function unwrap_key/5 can be used to
decrypt the key and load it into the session or token. Typically, these functions
are use to externalize keys (e.g. for storing them in a database), to import
pre-existing keys, or to migrate keys between tokens.
The set of supported combinations of wrapping key type, wrapped key type, and mechanism is limited and mostly specific to the token. Here are some examples that should be supported according to the PKCS#11 specification:
- Wrapping an AES secret with an AES secret key
- Wrapping a RSA or EC private key with an AES secret key
- Wrapping an RSA secret key or an AES secret key with an RSA public key
Typical mechanisms are:
{:ckm_aes_key_wrap_pad}{:ckm_rsa_pkcs}{:ckm_rsa_pkcs_oaep}
Wrapping keys must be marked as wrapping keys by setting the :cka_wrap attribute
to true. The target key must be marked as extractable by setting the
:cka_extractable attribute to true. There may be further restrictions depending
on the token.