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

attribute()

@type attribute() ::
  {atom()} | {atom(), binary()} | {atom(), integer()} | {atom(), boolean()}

attributes()

@type attributes() :: [attribute()]

mechanism_instance()

@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

close_all_sessions(module, slot_id)

close_session(session)

decrypt(session, mechanism, key, data)

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.

decrypt_final(session)

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

decrypt_init(session, mechanism, key)

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

decrypt_update(session, data)

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

destroy_object(session, object)

digest(session, data)

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

digest_final(session)

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

digest_init(session, mechanism)

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

digest_update(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(session, mechanism, key, data)

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.

encrypt_final(session)

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

encrypt_init(session, mechanism, key)

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

encrypt_update(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).

finalize(module)

find_objects(session, attributes, max_hits)

generate_key(session, mechanism, key_template)

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}
    ])

generate_key_pair(session, mechanism, pub_key_template, priv_key_template)

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

generate_random(session, len)

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

get_object_attributes(session, object, attribute_set)

list_mechanisms(module, slot_id)

list_slots(module, token_present)

load_module(path)

@spec load_module(String.t()) :: {:ok, Module.t()} | {:error, String.t()}

load_nifs()

mechanism_info(module, slot_id, mechanism_type)

open_session(module, slot_id, flags)

session_info(session)

session_login(session, user_type, pin)

@spec session_login(P11ex.Lib.SessionHandle.t(), atom(), binary()) ::
  :ok | {:error, atom()} | {:error, atom(), any()}

session_logout(session)

@spec session_logout(P11ex.Lib.SessionHandle.t()) ::
  :ok | {:error, atom()} | {:error, atom(), any()}

sign(session, data)

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

sign_final(session)

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

sign_init(session, mechanism, key)

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

sign_update(session, data)

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

token_info(module, slot_id)

verify(session, data, signature)

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

verify_init(session, mechanism, key)

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