Call logic of crypto subsystem in Linux kernel

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;
  • When a request is executed, a set of context information is maintained, which is recorded in the structure: struct crypto_tfm
  • crypto_tfm type pointer tfm can be understood as referring to an algorithm object
  • Reference link Linux kernel crypto folder cryptography knowledge learning_ CHYabc123456hh blog - CSDN blog
  • cra_init is a function to prepare the context. For example, if you use a hardware device to compress data, and the actual physical operation occurs on a queue of the hardware, you need to prepare the queue, prepare the necessary cache, and so on.
  • cra_exit is the exit context.
  • cra_u is the function that specifically executes the algorithm, such as the function that can be compressed and decompressed.
struct crypto_tfm {

	u32 crt_flags;

	int node;
	
	void (*exit)(struct crypto_tfm *tfm);
	
	struct crypto_alg *__crt_alg;

	void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};
  • crypto_ Last element in TFM__ crt_ctx is the private data for this context.
  • Crypto above_ CRA in alg_ The ctxsize parameter is the of this private data__ crt_ Size of CTX

Example: synchronization interface

/* Assign a compression and decompression context. You can see that the compression and decompression context here is crypto_tfm */
struct crypto_comp = crypto_alloc_comp(driver, type, mask);
    --> crypto_alloc_base(alg_name, type, mask)
            /* find algrithm: use alg_name, driver name */
        --> alg = crypto_alg_mod_lookup(alg_name, type, mask);
            /* Context is allocated according to specific algorithms */
        --> tfm = __crypto_alloc_tfm(alg, type, mask);
            /* Specifies the relevant algorithm in the context */
            --> tfm->__crt_alg = alg;
            --> crypto_init_ops
                /* Pass the compression and decompression function in the corresponding algorithm to the context */
                --> crypto_init_compress_ops(tfm)
                        /* ops is struct compress_tfm */
                --> ops->cot_compress = crypto_compress;
                        --> tfm->__crt_alg->cra_compress.coa_compress

                --> ops->cot_decompress = crypto_decompress;
        /*
        * In the final call to create context, the initialization function in the algorithm is a hardware.
        * Then the corresponding hardware initialization can be performed here.
        */
            --> if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))

The second is to perform compression:
crypto_comp_compress(tfm, input, ilen, result, &dlen)

crypto_comp_decompress(crypto_comp, src, slen, dst, dlen)
        /* so hardware can do compress here! */
    --> compress_tfm->cot_compress;

The third is to release the compressed context
crypto_free_comp(comp)
  • From the perspective of device driver, device driver only sees crypto_alg this structure.

  • Crypt in this structure_ Where does TFM, the context in which an operation is executed, come from? After all, crypto_alg is in this structure cra_ init, . cra_ exit, . cra_ In the U coa_compress requires this execution context.

  • Knowing these internal data structures is helpful for us to understand the external API. Now, assuming that the crypto device driver already exists, how can other kernel modules be used? In fact, we talked about crypto / testmgr at the beginning C) test procedure.

  • There are asynchronous test and synchronous test processes in the test code. Let's look at synchronous test first:

  • The main logic consists of three functions. First, you need to allocate a compressed context (the compressed example in this paper). In fact, it is crypto_tfm packaging, and cryto_tfm is the same: the purple is deleted. I can't find this compression example

struct crypto_comp {
	struct crypto_tfm base;
};
  • Use testmgr C file

Step 1: create an object {for preparation

  •  crypto_alloc_base via crypto_ alg_ mod_ Lookup judges that this algorithm type exists
  • To use the function__ crypto_alloc_tfm creates objects for it

The second step is to perform specific function operations

Step 3: release the compressed context

Example asynchronous interface

  • testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin
  • The difference between asynchronous interface and synchronous interface is that an acomp is created here_ The context of req and subsequent operations are carried out around this req structure.
  • You can see that req contains the callback functions required by the asynchronous interface
static int test_acomp(struct crypto_acomp *tfm,
			      const struct comp_testvec *ctemplate,
		      const struct comp_testvec *dtemplate,
		      int ctcount, int dtcount)
{
	const char *algo = crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm));
	unsigned int i;
	char *output, *decomp_out;
	int ret;
	struct scatterlist src, dst;
	struct acomp_req *req;
	struct crypto_wait wait;

	output = kmalloc(COMP_BUF_SIZE, GFP_KERNEL);
	if (!output)
		return -ENOMEM;

	decomp_out = kmalloc(COMP_BUF_SIZE, GFP_KERNEL);
	if (!decomp_out) {
		kfree(output);
		return -ENOMEM;
	}

	for (i = 0; i < ctcount; i++) {
		unsigned int dlen = COMP_BUF_SIZE;
		int ilen = ctemplate[i].inlen;
		void *input_vec;

		input_vec = kmemdup(ctemplate[i].input, ilen, GFP_KERNEL);
		if (!input_vec) {
			ret = -ENOMEM;
			goto out;
		}

		memset(output, 0, dlen);
		crypto_init_wait(&wait);
		sg_init_one(&src, input_vec, ilen);
		sg_init_one(&dst, output, dlen);

		req = acomp_request_alloc(tfm);
		if (!req) {
			pr_err("alg: acomp: request alloc failed for %s\n",
			       algo);
			kfree(input_vec);
			ret = -ENOMEM;
			goto out;
		}

		acomp_request_set_params(req, &src, &dst, ilen, dlen);
		acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
					   crypto_req_done, &wait);

		ret = crypto_wait_req(crypto_acomp_compress(req), &wait);
		if (ret) {
			pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
			       i + 1, algo, -ret);
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		ilen = req->dlen;
		dlen = COMP_BUF_SIZE;
		sg_init_one(&src, output, ilen);
		sg_init_one(&dst, decomp_out, dlen);
		crypto_init_wait(&wait);
		acomp_request_set_params(req, &src, &dst, ilen, dlen);

		ret = crypto_wait_req(crypto_acomp_decompress(req), &wait);
		if (ret) {
			pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
			       i + 1, algo, -ret);
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		if (req->dlen != ctemplate[i].inlen) {
			pr_err("alg: acomp: Compression test %d failed for %s: output len = %d\n",
			       i + 1, algo, req->dlen);
			ret = -EINVAL;
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		if (memcmp(input_vec, decomp_out, req->dlen)) {
			pr_err("alg: acomp: Compression test %d failed for %s\n",
			       i + 1, algo);
			hexdump(output, req->dlen);
			ret = -EINVAL;
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		kfree(input_vec);
		acomp_request_free(req);
	}

	for (i = 0; i < dtcount; i++) {
		unsigned int dlen = COMP_BUF_SIZE;
		int ilen = dtemplate[i].inlen;
		void *input_vec;

		input_vec = kmemdup(dtemplate[i].input, ilen, GFP_KERNEL);
		if (!input_vec) {
			ret = -ENOMEM;
			goto out;
		}

		memset(output, 0, dlen);
		crypto_init_wait(&wait);
		sg_init_one(&src, input_vec, ilen);
		sg_init_one(&dst, output, dlen);

		req = acomp_request_alloc(tfm);
		if (!req) {
			pr_err("alg: acomp: request alloc failed for %s\n",
			       algo);
			kfree(input_vec);
			ret = -ENOMEM;
			goto out;
		}

		acomp_request_set_params(req, &src, &dst, ilen, dlen);
		acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
					   crypto_req_done, &wait);

		ret = crypto_wait_req(crypto_acomp_decompress(req), &wait);
		if (ret) {
			pr_err("alg: acomp: decompression failed on test %d for %s: ret=%d\n",
			       i + 1, algo, -ret);
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		if (req->dlen != dtemplate[i].outlen) {
			pr_err("alg: acomp: Decompression test %d failed for %s: output len = %d\n",
			       i + 1, algo, req->dlen);
			ret = -EINVAL;
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		if (memcmp(output, dtemplate[i].output, req->dlen)) {
			pr_err("alg: acomp: Decompression test %d failed for %s\n",
			       i + 1, algo);
			hexdump(output, req->dlen);
			ret = -EINVAL;
			kfree(input_vec);
			acomp_request_free(req);
			goto out;
		}

		kfree(input_vec);
		acomp_request_free(req);
	}

	ret = 0;

out:
	kfree(decomp_out);
	kfree(output);
	return ret;
}

  • It should be noted here that testmsg The acomp test program in C adds wait related content. This should be added for the convenience of testing. In a general asynchronous interface, when the hardware completes the operation, it is OK to directly call the callback function of the asynchronous interface in the interrupt function.
  • request is created for asynchrony, but the main purpose of the wait object is unclear

An example of an exception

 

Generally speaking, the process of using encryption algorithm in kernel mode is divided into the following steps:

  • Allocating tranform objects {is the specific algorithm
  • Allocate request object # asynchronous operation waiting object
  • Set the context, such as encryption key / signature verification public key, fill in the data source, set the buffer for scatterlist, set the callback function / initialization vector for asynchronous request object, and set the key for cryptographic algorithm object
  • Complete encryption / decryption / summary / signature verification
  • Release objects such as transform and request
  • If it is called synchronously, you do not need to create a request object

Reference link

Keywords: Linux Crypto

Added by spfoonnewb on Tue, 04 Jan 2022 18:36:15 +0200