Introduction to the Linux Kernel Crypto API

Overview

The Linux Kernel Crypto API provides a rich set of encryption algorithms and other data transformation mechanisms, defining a unified method for invoking these mechanisms.

In the Kernel Crypto API, all algorithms are collectively referred to as “transformations”; thus, handle variables related to cryptographic algorithms are typically named <span>tfm</span>. In addition to encryption operations, this API also supports compression transformations, which are handled in a manner consistent with cryptographic algorithms.

The Kernel Crypto API primarily serves two types of entities:

  • Consumers: Modules or applications that request encryption services;
  • Transformation Implementations: Code or hardware that provides specific data transformation functions (usually cryptographic algorithms) that can be invoked by consumers through the API.

To standardize terminology, the Kernel Crypto API clearly defines the following concepts:

  • Transformation Implementation: Refers to the code or hardware interface that actually performs the transformation, whose behavior strictly adheres to the algorithm specification. For example, an implementation compliant with the AES standard is a transformation implementation.
  • Transformation Object: An instance of a transformation implementation. A transformation implementation can correspond to multiple transformation objects, each held by a consumer of the cryptographic API or other transformations. Transformation objects are allocated upon consumer requests and returned to consumers through calls like <span>tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC)</span>.
  • Transformation Context: Private data associated with the transformation object, used to maintain runtime state information.

Definition of Crypto Algorithms

The structure <span>struct crypto_alg</span> is a generic base structure that describes Crypto API algorithms, and any algorithm must populate this structure before registration. Its definition is as follows:

struct crypto_alg {
 struct list_head cra_list;
 struct list_head cra_users;

 u32 cra_flags;
 unsigned int cra_blocksize;
 unsigned int cra_ctxsize;
 unsigned int cra_alignmask;

 int cra_priority;
 refcount_t cra_refcnt;

 char cra_name[CRYPTO_MAX_ALG_NAME];
 char cra_driver_name[CRYPTO_MAX_ALG_NAME];

 const struct crypto_type *cra_type;

 union {
 struct cipher_alg cipher;
 struct compress_alg compress;
 } cra_u;

 int (*cra_init)(struct crypto_tfm *tfm);
 void (*cra_exit)(struct crypto_tfm *tfm);
 void (*cra_destroy)(struct crypto_alg *alg);

 struct module *cra_module;

 #ifdef CONFIG_CRYPTO_STATS
 union {
 struct crypto_istat_aead aead;
 struct crypto_istat_akcipher akcipher;
 struct crypto_istat_cipher cipher;
 struct crypto_istat_compress compress;
 struct crypto_istat_hash hash;
 struct crypto_istat_rng rng;
 struct crypto_istat_kpp kpp;
 } stats;
 #endif /* CONFIG_CRYPTO_STATS */

} CRYPTO_MINALIGN_ATTR;
  • cra_flags: Transformation flags used to fine-tune the description of the algorithm transformation.
  • cra_blocksize: The minimum block size (in bytes) for this transformation, indicating the smallest data unit that the algorithm can process. For HASH transformations, blocks smaller than <span>cra_blocksize</span> are allowed; however, for other algorithm transformation types, passing smaller blocks will return an error.
  • cra_ctxsize: The size of the transformation operation context. It informs the Kernel Crypto API of the memory size to allocate for the transformation context.
  • cra_alignmask: The alignment mask for input/output data buffers. Note that under certain conditions, the Crypto API will realign processing in software, but this incurs a performance penalty. This alignment design addresses the limitation of hardware that cannot read data from arbitrary addresses.
  • cra_priority: The priority of the transformation implementation. When multiple transformations with the same <span>cra_name</span> are available, the kernel will select the one with the highest priority.
  • cra_name: The generic name of the transformation algorithm (allowing multiple implementations to share). This is the name of the algorithm itself, and the kernel uses this field to look up the provider of a specific transformation.
  • cra_driver_name: The unique name of the transformation provider. Typically a combination of the chip or provider name and the algorithm name.
  • cra_type: The type of cryptographic algorithm transformation. Points to the implementation’s generic callback <span>struct crypto_type</span>.
  • cra_u: A collection of callback functions that implement the transformation.
  • cra_init: Initializes the cryptographic transformation object. Called only once during instantiation to handle special hardware requirements or set up software fallbacks.
  • cra_exit: Releases the cryptographic transformation object.
  • cra_module: The owner of the transformation implementation.
  • stats: A union of all <span>crypto_istat_xxx</span> statistical structures.

Note that other members of this structure are for internal use by the Crypto API and should not be used by algorithm implementers.

The registration interface for cryptographic algorithms is <span>crypto_register_alg()</span><span>, which primarily performs parameter validation, registration, and self-testing:</span>

int crypto_register_alg(struct crypto_alg *alg)
{
 struct crypto_larval *larval;
 bool test_started;
 int err;

 alg->cra_flags &= ~CRYPTO_ALG_DEAD;
 err = crypto_check_alg(alg);
 if (err)
 return err;

 down_write(&crypto_alg_sem);
 larval = __crypto_register_alg(alg);
 test_started = static_key_enabled(&crypto_boot_test_finished);
 if (!IS_ERR_OR_NULL(larval))
 larval->test_started = test_started;
 up_write(&crypto_alg_sem);

 if (IS_ERR_OR_NULL(larval))
 return PTR_ERR(larval);

 if (test_started)
 crypto_wait_for_test(larval);
 return 0;
}
EXPORT_SYMBOL_GPL(crypto_register_alg);

Correspondingly, the unregistration interface is <span>crypto_unregister_alg()</span><span>, which is used to remove algorithms and free resources:</span>

void crypto_unregister_alg(struct crypto_alg *alg)
{
 int ret;
 LIST_HEAD(list);

 down_write(&crypto_alg_sem);
 ret = crypto_remove_alg(alg, &list);
 up_write(&crypto_alg_sem);

 if (WARN(ret, "Algorithm %s is not registered", alg->cra_driver_name))
 return;

 if (WARN_ON(refcount_read(&alg->cra_refcnt) != 1))
 return;

 if (alg->cra_destroy)
 alg->cra_destroy(alg);

 crypto_remove_final(&list);
}
EXPORT_SYMBOL_GPL(crypto_unregister_alg);

Hash Algorithms

Hash algorithms in the Linux Kernel Crypto framework are divided into synchronous and asynchronous execution modes:

  • Synchronous Mode: The caller must wait for the operation to complete.
  • Asynchronous Mode: Requests are queued, and the caller is notified via a callback upon completion.

Taking asynchronous hashing as an example, the algorithm structure is defined as follows:

struct ahash_alg {
 int (*init)(struct ahash_request *req);
 int (*update)(struct ahash_request *req);
 int (*final)(struct ahash_request *req);
 int (*finup)(struct ahash_request *req);
 int (*digest)(struct ahash_request *req);
 int (*export)(struct ahash_request *req, void *out);
 int (*import)(struct ahash_request *req, const void *in);
 int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
 unsigned int keylen);
 int (*init_tfm)(struct crypto_ahash *tfm);
 void (*exit_tfm)(struct crypto_ahash *tfm);

 struct hash_alg_common halg;
};

The structure <span>struct ahash_alg</span> describes the implementation of asynchronous hash algorithms, defining the callbacks and algorithm properties that the driver needs to implement.

  • init: Initializes the hash context (called for each new request).
  • update: Processes input data blocks, updating the hash state (can be called multiple times).
  • final: Ends the hash computation and outputs the final digest.
  • finup: A combination of <span>update</span> + <span>final</span>, suitable for scenarios where some hardware can only complete in one step.
  • digest: A combination of <span>init</span> + <span>update</span> + <span>final</span>, completing all operations in one step.
  • export/import: Export/import the intermediate state of the hash, facilitating saving/restoring computation progress.
  • setkey: Sets the key for the hash algorithm (only for hash algorithms requiring keys like HMAC/CMAC; ordinary hashes can be NULL).
  • init_tfm: Initializes the algorithm instance. Called only once during instantiation to check for special hardware requirements and set up software fallback schemes.
  • exit_tfm: Releases the algorithm instance.
  • halg: General properties of the algorithm.

The structure <span>struct hash_alg_common</span> defines the general properties of hash algorithms:

struct hash_alg_common {
 unsigned int digestsize;
 unsigned int statesize;

 struct crypto_alg base;
};
  • digestsize: The size of the transformation result, i.e., the length of the message digest.
  • statesize: The block size of the intermediate state in the transformation, which must provide a buffer of this size for the export function.
  • base: The general structure of the cryptographic algorithm, with <span>hash_alg_common</span><span> adding hash-specific information.</span>

The interface for hash algorithms is called by other kernel modules, as illustrated below:

I)   DATA -----------.
                    v
     .init() -> .update() -> .final()      ! .update() might not be called
                 ^    |         |            at all in this scenario.
                 '----'         '---> HASH

II)  DATA -----------.-----------.
                    v           v
     .init() -> .update() -> .finup()      ! .update() may not be called
                 ^    |         |            at all in this scenario.
                 '----'         '---> HASH

III) DATA -----------.
                    v
                .digest()                  ! The entire process is handled
                    |                        by the .digest() call.
                    '---------------> HASH

If you want to use <span>.export</span> and <span>.import</span> functions, the calling diagram is as follows:

KEY--.                 DATA--.
    v                       v                  ! .update() may not be called
.setkey() -> .init() -> .update() -> .export()   at all in this scenario.
                     ^     |         |
                     '-----'         '--> PARTIAL_HASH

----------- other transformations happen here -----------

PARTIAL_HASH--.   DATA1--.
             v          v
         .import -> .update() -> .final()     ! .update() may not be called
                 ^    |         |           at all in this scenario.
                 '----'         '--> HASH1

PARTIAL_HASH--.   DATA2-.
             v         v
         .import -> .finup()
                   |
                   '---------------> HASH2

The message digest API and hash request API are both defined in <span> include/crypto/hash.h</span><span> file.</span>

The registration and unregistration functions for hash algorithms are implemented based on the general cryptographic algorithm interface:

static int ahash_prepare_alg(struct ahash_alg *alg)
{
 struct crypto_alg *base = &alg->halg.base;

 if (alg->halg.digestsize > HASH_MAX_DIGESTSIZE ||
      alg->halg.statesize > HASH_MAX_STATESIZE ||
      alg->halg.statesize == 0)
 return -EINVAL;

 base->cra_type = &crypto_ahash_type;
 base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
 base->cra_flags |= CRYPTO_ALG_TYPE_AHASH;

 return 0;
}

int crypto_register_ahash(struct ahash_alg *alg)
{
 struct crypto_alg *base = &alg->halg.base;
 int err;

 err = ahash_prepare_alg(alg);
 if (err)
 return err;

 return crypto_register_alg(base);
}
EXPORT_SYMBOL_GPL(crypto_register_ahash);

void crypto_unregister_ahash(struct ahash_alg *alg)
{
 crypto_unregister_alg(&alg->halg.base);
}
EXPORT_SYMBOL_GPL(crypto_unregister_ahash);

Symmetric Encryption Algorithms

The simplest symmetric encryption algorithm is single block encryption and decryption, such as the **ecb(aes)** algorithm, which processes only a single data block at a time with no dependencies between blocks.

The definition of symmetric encryption algorithms is as follows:

struct skcipher_alg {
 int (*setkey)(struct crypto_skcipher *tfm, const u8 *key,
               unsigned int keylen);
 int (*encrypt)(struct skcipher_request *req);
 int (*decrypt)(struct skcipher_request *req);
 int (*init)(struct crypto_skcipher *tfm);
 void (*exit)(struct crypto_skcipher *tfm);

 unsigned int min_keysize;
 unsigned int max_keysize;
 unsigned int ivsize;
 unsigned int chunksize;
 unsigned int walksize;

 struct crypto_alg base;
};
  • setkey: Sets the key, used to write the provided key into hardware or store it in the transformation context.
  • encrypt: Used for encrypting data blocks in a scatterlist.
  • decrypt: Used for decrypting data blocks in a scatterlist.
  • init: Initializes the cryptographic transformation object. Called only once during instantiation, immediately after allocating the transformation context.
  • exit: Releases the cryptographic transformation object.
  • min_keysize: The minimum key length supported by the algorithm.
  • max_keysize: The maximum key length supported by the algorithm.
  • ivsize: The length of the initialization vector (IV).
  • chunksize: Usually equal to the block size, but for stream cipher algorithms like CTR, it is set to the underlying block size.
  • walksize: Usually equal to <span>chunksize</span>, unless the algorithm significantly improves efficiency when processing multiple blocks in parallel.
  • base: Definition of the general cryptographic algorithm.

All fields in the structure of symmetric encryption algorithms must be configured, except for the <span>ivsize</span> variable.

The interface for symmetric encryption algorithms is called by other kernel modules, as illustrated below:

::

             KEY ---.    PLAINTEXT ---.
                    v                 v
              .cia_setkey() -> .cia_encrypt()
                                      |
                                      '-----> CIPHERTEXT

Additionally, the <span>.cra_setkey</span> function may be called multiple times:

::

      KEY1 --.    PLAINTEXT1 --.         KEY2 --.    PLAINTEXT2 --.
             v                 v                v                 v
       .cia_setkey() -> .cia_encrypt() -> .cia_setkey() -> .cia_encrypt()
                               |                                  |
                               '---> CIPHERTEXT1                  '---> CIPHERTEXT2

Symmetric encryption algorithms also support multi-block cipher transformations, such as the **cbc(aes)** algorithm, which processes scatterlists passed to the transformation function and outputs the results to another scatterlist. If the cryptographic algorithm implementation requires data to be strictly aligned, the caller should invoke the <span>crypto_skcipher_alignmask()</span><span> function to obtain the alignment value. Although the kernel cryptographic API can handle unaligned requests, this incurs additional overhead as the kernel needs to perform data realignment operations, which may involve data movement.</span>

The symmetric encryption API and request API are both defined in <span> include/crypto/skcipher.h</span><span> file.</span>

Similarly, the registration and unregistration functions for symmetric encryption algorithms primarily also call the cryptographic algorithm interface, implemented as follows:

int crypto_register_skcipher(struct skcipher_alg *alg)
{
 struct crypto_alg *base = &alg->base;
 int err;

 err = skcipher_prepare_alg(alg);
 if (err)
 return err;

 return crypto_register_alg(base);
}
EXPORT_SYMBOL_GPL(crypto_register_skcipher);

void crypto_unregister_skcipher(struct skcipher_alg *alg)
{
 crypto_unregister_alg(&alg->base);
}
EXPORT_SYMBOL_GPL(crypto_unregister_skcipher);

int crypto_register_skciphers(struct skcipher_alg *algs, int count)
{
 int i, ret;

 for (i = 0; i < count; i++) {
   ret = crypto_register_skcipher(&algs[i]);
 if (ret)
   goto err;
 }

 return 0;

err:
 for (--i; i >= 0; --i)
  crypto_unregister_skcipher(&algs[i]);

 return ret;
}
EXPORT_SYMBOL_GPL(crypto_register_skciphers);

void crypto_unregister_skciphers(struct skcipher_alg *algs, int count)
{
 int i;

 for (i = count - 1; i >= 0; --i)
  crypto_unregister_skcipher(&algs[i]);
}
EXPORT_SYMBOL_GPL(crypto_unregister_skciphers);

Asymmetric Encryption Algorithms

Asymmetric encryption algorithms use public and private keys for encryption and decryption, such as RSA and ECC algorithms. The definition of asymmetric encryption algorithms is as follows:

struct akcipher_alg {
 int (*sign)(struct akcipher_request *req);
 int (*verify)(struct akcipher_request *req);
 int (*encrypt)(struct akcipher_request *req);
 int (*decrypt)(struct akcipher_request *req);
 int (*set_pub_key)(struct crypto_akcipher *tfm, const void *key,
       unsigned int keylen);
 int (*set_priv_key)(struct crypto_akcipher *tfm, const void *key,
        unsigned int keylen);
 unsigned int (*max_size)(struct crypto_akcipher *tfm);
 int (*init)(struct crypto_akcipher *tfm);
 void (*exit)(struct crypto_akcipher *tfm);

 unsigned int reqsize;
 struct crypto_alg base;
};
  • sign: Signature operation.
  • verify: Signature verification operation.
  • encrypt: Encryption operation.
  • decrypt: Decryption operation.
  • set_pub_key: Sets the public key, parsing the DER-encoded public key and parameters.
  • set_priv_key: Sets the private key, parsing the DER-encoded private key and parameters.
  • max_size: Returns the target buffer size required for the specified key.
  • init: Initializes the cryptographic transformation object. Called only once during instantiation, immediately after allocating the transformation context.
  • exit: Releases the cryptographic transformation object.
  • reqsize: The size of the required request context.
  • base: Definition of the general cryptographic algorithm.

The asymmetric encryption API and request API are both defined in <span> include/crypto/akcipher.h</span><span> file.</span>

Similarly, the registration and unregistration functions for asymmetric encryption algorithms primarily also call the cryptographic algorithm interface, implemented as follows:

static void akcipher_prepare_alg(struct akcipher_alg *alg)
{
 struct crypto_alg *base = &alg->base;

 base->cra_type = &crypto_akcipher_type;
 base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
 base->cra_flags |= CRYPTO_ALG_TYPE_AKCIPHER;
}

int crypto_register_akcipher(struct akcipher_alg *alg)
{
 struct crypto_alg *base = &alg->base;

 if (!alg->sign)
  alg->sign = akcipher_default_op;
 if (!alg->verify)
  alg->verify = akcipher_default_op;
 if (!alg->encrypt)
  alg->encrypt = akcipher_default_op;
 if (!alg->decrypt)
  alg->decrypt = akcipher_default_op;
 if (!alg->set_priv_key)
  alg->set_priv_key = akcipher_default_set_key;

 akcipher_prepare_alg(alg);
 return crypto_register_alg(base);
}
EXPORT_SYMBOL_GPL(crypto_register_akcipher);

Example Usage of the Crypto API

Below is an example of how to use the Crypto API; more examples can be found in the regression testing module <span>tcrypt.c</span><span>.</span>

#include <crypto/hash.h>
#include <linux/err.h>
#include <linux/scatterlist.h>

struct scatterlist sg[2];
char result[128];
struct crypto_ahash *tfm;
struct ahash_request *req;

tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
    fail();

/* ... set up the scatterlists ... */

req = ahash_request_alloc(tfm, GFP_ATOMIC);
if (!req)
    fail();

ahash_request_set_callback(req, 0, NULL, NULL);
ahash_request_set_crypt(req, sg, result, 2);

if (crypto_ahash_digest(req))
    fail();

ahash_request_free(req);
crypto_free_ahash(tfm);
  1. First, call <span>crypto_alloc_ahash</span> to allocate an asynchronous hash MD5 transformation object, and call <span>ahash_request_alloc</span> to allocate memory for the data request structure;
  2. Then call <span>ahash_request_set_callback</span> to set the asynchronous processing callback function;
  3. Next, call <span>ahash_request_set_crypt</span> to bind the input data (<span>sg</span> scatterlist) and output buffer (<span>result</span>);
  4. Then call <span>crypto_ahash_digest</span> to trigger a one-time hash computation;
  5. Finally, call <span>ahash_request_free</span> and <span>crypto_free_ahash</span> to release the request object and transformation object to avoid memory leaks.

Leave a Comment